diff options
Diffstat (limited to 'src/com/android/messaging/datamodel/data/DraftMessageData.java')
-rw-r--r-- | src/com/android/messaging/datamodel/data/DraftMessageData.java | 855 |
1 files changed, 0 insertions, 855 deletions
diff --git a/src/com/android/messaging/datamodel/data/DraftMessageData.java b/src/com/android/messaging/datamodel/data/DraftMessageData.java deleted file mode 100644 index 7a7199a..0000000 --- a/src/com/android/messaging/datamodel/data/DraftMessageData.java +++ /dev/null @@ -1,855 +0,0 @@ -/* - * Copyright (C) 2015 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.messaging.datamodel.data; - -import android.net.Uri; -import android.text.TextUtils; - -import com.android.messaging.datamodel.MessageTextStats; -import com.android.messaging.datamodel.action.ReadDraftDataAction; -import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener; -import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionMonitor; -import com.android.messaging.datamodel.action.WriteDraftMessageAction; -import com.android.messaging.datamodel.binding.BindableData; -import com.android.messaging.datamodel.binding.Binding; -import com.android.messaging.datamodel.binding.BindingBase; -import com.android.messaging.sms.MmsConfig; -import com.android.messaging.sms.MmsSmsUtils; -import com.android.messaging.sms.MmsUtils; -import com.android.messaging.util.Assert; -import com.android.messaging.util.Assert.DoesNotRunOnMainThread; -import com.android.messaging.util.Assert.RunsOnMainThread; -import com.android.messaging.util.BugleGservices; -import com.android.messaging.util.BugleGservicesKeys; -import com.android.messaging.util.LogUtil; -import com.android.messaging.util.PhoneUtils; -import com.android.messaging.util.SafeAsyncTask; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -public class DraftMessageData extends BindableData implements ReadDraftDataActionListener { - - /** - * Interface for DraftMessageData listeners - */ - public interface DraftMessageDataListener { - @RunsOnMainThread - void onDraftChanged(DraftMessageData data, int changeFlags); - - @RunsOnMainThread - void onDraftAttachmentLimitReached(DraftMessageData data); - - @RunsOnMainThread - void onDraftAttachmentLoadFailed(); - } - - /** - * Interface for providing subscription-related data to DraftMessageData - */ - public interface DraftMessageSubscriptionDataProvider { - int getConversationSelfSubId(); - } - - // Flags sent to onDraftChanged to help the receiver limit the amount of work done - public static int ATTACHMENTS_CHANGED = 0x0001; - public static int MESSAGE_TEXT_CHANGED = 0x0002; - public static int MESSAGE_SUBJECT_CHANGED = 0x0004; - // Whether the self participant data has been loaded - public static int SELF_CHANGED = 0x0008; - public static int ALL_CHANGED = 0x00FF; - // ALL_CHANGED intentionally doesn't include WIDGET_CHANGED. ConversationFragment needs to - // be notified if the draft it is looking at is changed externally (by a desktop widget) so it - // can reload the draft. - public static int WIDGET_CHANGED = 0x0100; - - private final String mConversationId; - private ReadDraftDataActionMonitor mMonitor; - private final DraftMessageDataEventDispatcher mListeners; - private DraftMessageSubscriptionDataProvider mSubscriptionDataProvider; - - private boolean mIncludeEmailAddress; - private boolean mIsGroupConversation; - private String mMessageText; - private String mMessageSubject; - private String mSelfId; - private MessageTextStats mMessageTextStats; - private boolean mSending; - - /** Keeps track of completed attachments in the message draft. This data is persisted to db */ - private final List<MessagePartData> mAttachments; - - /** A read-only wrapper on mAttachments surfaced to the UI layer for rendering */ - private final List<MessagePartData> mReadOnlyAttachments; - - /** Keeps track of pending attachments that are being loaded. The pending attachments are - * transient, because they are not persisted to the database and are dropped once we go - * to the background (after the UI calls saveToStorage) */ - private final List<PendingAttachmentData> mPendingAttachments; - - /** A read-only wrapper on mPendingAttachments surfaced to the UI layer for rendering */ - private final List<PendingAttachmentData> mReadOnlyPendingAttachments; - - /** Is the current draft a cached copy of what's been saved to the database. If so, we - * may skip loading from database if we are still bound */ - private boolean mIsDraftCachedCopy; - - /** Whether we are currently asynchronously validating the draft before sending. */ - private CheckDraftForSendTask mCheckDraftForSendTask; - - public DraftMessageData(final String conversationId) { - mConversationId = conversationId; - mAttachments = new ArrayList<MessagePartData>(); - mReadOnlyAttachments = Collections.unmodifiableList(mAttachments); - mPendingAttachments = new ArrayList<PendingAttachmentData>(); - mReadOnlyPendingAttachments = Collections.unmodifiableList(mPendingAttachments); - mListeners = new DraftMessageDataEventDispatcher(); - mMessageTextStats = new MessageTextStats(); - } - - public void addListener(final DraftMessageDataListener listener) { - mListeners.add(listener); - } - - public void setSubscriptionDataProvider(final DraftMessageSubscriptionDataProvider provider) { - mSubscriptionDataProvider = provider; - } - - public void updateFromMessageData(final MessageData message, final String bindingId) { - // New attachments have arrived - only update if the user hasn't already edited - Assert.notNull(bindingId); - // The draft is now synced with actual MessageData and no longer a cached copy. - mIsDraftCachedCopy = false; - // Do not use the loaded draft if the user began composing a message before the draft loaded - // During config changes (orientation), the text fields preserve their data, so allow them - // to be the same and still consider the draft unchanged by the user - if (isDraftEmpty() || (TextUtils.equals(mMessageText, message.getMessageText()) && - TextUtils.equals(mMessageSubject, message.getMmsSubject()) && - mAttachments.isEmpty())) { - // No need to clear as just checked it was empty or a subset - setMessageText(message.getMessageText(), false /* notify */); - setMessageSubject(message.getMmsSubject(), false /* notify */); - for (final MessagePartData part : message.getParts()) { - if (part.isAttachment() && getAttachmentCount() >= getAttachmentLimit()) { - dispatchAttachmentLimitReached(); - break; - } - - if (part instanceof PendingAttachmentData) { - // This is a pending attachment data from share intent (e.g. an shared image - // that we need to persist locally). - final PendingAttachmentData data = (PendingAttachmentData) part; - Assert.equals(PendingAttachmentData.STATE_PENDING, data.getCurrentState()); - addOnePendingAttachmentNoNotify(data, bindingId); - } else if (part.isAttachment()) { - addOneAttachmentNoNotify(part); - } - } - dispatchChanged(ALL_CHANGED); - } else { - // The user has started a new message so we throw out the draft message data if there - // is one but we also loaded the self metadata and need to let our listeners know. - dispatchChanged(SELF_CHANGED); - } - } - - /** - * Create a MessageData object containing a copy of all the parts in this DraftMessageData. - * - * @param clearLocalCopy whether we should clear out the in-memory copy of the parts. If we - * are simply pausing/resuming and not sending the message, then we can keep - * @return the MessageData for the draft, null if self id is not set - */ - public MessageData createMessageWithCurrentAttachments(final boolean clearLocalCopy) { - MessageData message = null; - if (getIsMms()) { - message = MessageData.createDraftMmsMessage(mConversationId, mSelfId, - mMessageText, mMessageSubject); - for (final MessagePartData attachment : mAttachments) { - message.addPart(attachment); - } - } else { - message = MessageData.createDraftSmsMessage(mConversationId, mSelfId, - mMessageText); - } - - if (clearLocalCopy) { - // The message now owns all the attachments and the text... - clearLocalDraftCopy(); - dispatchChanged(ALL_CHANGED); - } else { - // The draft message becomes a cached copy for UI. - mIsDraftCachedCopy = true; - } - return message; - } - - private void clearLocalDraftCopy() { - mIsDraftCachedCopy = false; - mAttachments.clear(); - setMessageText(""); - setMessageSubject(""); - } - - public String getConversationId() { - return mConversationId; - } - - public String getMessageText() { - return mMessageText; - } - - public String getMessageSubject() { - return mMessageSubject; - } - - public boolean getIsMms() { - final int selfSubId = getSelfSubId(); - return MmsSmsUtils.getRequireMmsForEmailAddress(mIncludeEmailAddress, selfSubId) || - (mIsGroupConversation && MmsUtils.groupMmsEnabled(selfSubId)) || - mMessageTextStats.getMessageLengthRequiresMms() || !mAttachments.isEmpty() || - !TextUtils.isEmpty(mMessageSubject); - } - - public boolean getIsGroupMmsConversation() { - return getIsMms() && mIsGroupConversation; - } - - public String getSelfId() { - return mSelfId; - } - - public int getNumMessagesToBeSent() { - return mMessageTextStats.getNumMessagesToBeSent(); - } - - public int getCodePointsRemainingInCurrentMessage() { - return mMessageTextStats.getCodePointsRemainingInCurrentMessage(); - } - - public int getSelfSubId() { - return mSubscriptionDataProvider == null ? ParticipantData.DEFAULT_SELF_SUB_ID : - mSubscriptionDataProvider.getConversationSelfSubId(); - } - - private void setMessageText(final String messageText, final boolean notify) { - mMessageText = messageText; - mMessageTextStats.updateMessageTextStats(getSelfSubId(), mMessageText); - if (notify) { - dispatchChanged(MESSAGE_TEXT_CHANGED); - } - } - - private void setMessageSubject(final String subject, final boolean notify) { - mMessageSubject = subject; - if (notify) { - dispatchChanged(MESSAGE_SUBJECT_CHANGED); - } - } - - public void setMessageText(final String messageText) { - setMessageText(messageText, false); - } - - public void setMessageSubject(final String subject) { - setMessageSubject(subject, false); - } - - public void addAttachments(final Collection<? extends MessagePartData> attachments) { - // If the incoming attachments contains a single-only attachment, we need to clear - // the existing attachments. - for (final MessagePartData data : attachments) { - if (data.isSinglePartOnly()) { - // clear any existing attachments because the attachment we're adding can only - // exist by itself. - destroyAttachments(); - break; - } - } - // If the existing attachments contain a single-only attachment, we need to clear the - // existing attachments to make room for the incoming attachment. - for (final MessagePartData data : mAttachments) { - if (data.isSinglePartOnly()) { - // clear any existing attachments because the single attachment can only exist - // by itself - destroyAttachments(); - break; - } - } - // If any of the pending attachments contain a single-only attachment, we need to clear the - // existing attachments to make room for the incoming attachment. - for (final MessagePartData data : mPendingAttachments) { - if (data.isSinglePartOnly()) { - // clear any existing attachments because the single attachment can only exist - // by itself - destroyAttachments(); - break; - } - } - - boolean reachedLimit = false; - for (final MessagePartData data : attachments) { - // Don't break out of loop even if limit has been reached so we can destroy all - // of the over-limit attachments. - reachedLimit |= addOneAttachmentNoNotify(data); - } - if (reachedLimit) { - dispatchAttachmentLimitReached(); - } - dispatchChanged(ATTACHMENTS_CHANGED); - } - - public boolean containsAttachment(final Uri contentUri) { - for (final MessagePartData existingAttachment : mAttachments) { - if (existingAttachment.getContentUri().equals(contentUri)) { - return true; - } - } - - for (final PendingAttachmentData pendingAttachment : mPendingAttachments) { - if (pendingAttachment.getContentUri().equals(contentUri)) { - return true; - } - } - return false; - } - - /** - * Try to add one attachment to the attachment list, while guarding against duplicates and - * going over the limit. - * @return true if the attachment limit was reached, false otherwise - */ - private boolean addOneAttachmentNoNotify(final MessagePartData attachment) { - Assert.isTrue(attachment.isAttachment()); - final boolean reachedLimit = getAttachmentCount() >= getAttachmentLimit(); - if (reachedLimit || containsAttachment(attachment.getContentUri())) { - // Never go over the limit. Never add duplicated attachments. - attachment.destroyAsync(); - return reachedLimit; - } else { - addAttachment(attachment, null /*pendingAttachment*/); - return false; - } - } - - private void addAttachment(final MessagePartData attachment, - final PendingAttachmentData pendingAttachment) { - if (attachment != null && attachment.isSinglePartOnly()) { - // clear any existing attachments because the attachment we're adding can only - // exist by itself. - destroyAttachments(); - } - if (pendingAttachment != null && pendingAttachment.isSinglePartOnly()) { - // clear any existing attachments because the attachment we're adding can only - // exist by itself. - destroyAttachments(); - } - // If the existing attachments contain a single-only attachment, we need to clear the - // existing attachments to make room for the incoming attachment. - for (final MessagePartData data : mAttachments) { - if (data.isSinglePartOnly()) { - // clear any existing attachments because the single attachment can only exist - // by itself - destroyAttachments(); - break; - } - } - // If any of the pending attachments contain a single-only attachment, we need to clear the - // existing attachments to make room for the incoming attachment. - for (final MessagePartData data : mPendingAttachments) { - if (data.isSinglePartOnly()) { - // clear any existing attachments because the single attachment can only exist - // by itself - destroyAttachments(); - break; - } - } - if (attachment != null) { - mAttachments.add(attachment); - } else if (pendingAttachment != null) { - mPendingAttachments.add(pendingAttachment); - } - } - - public void addPendingAttachment(final PendingAttachmentData pendingAttachment, - final BindingBase<DraftMessageData> binding) { - final boolean reachedLimit = addOnePendingAttachmentNoNotify(pendingAttachment, - binding.getBindingId()); - if (reachedLimit) { - dispatchAttachmentLimitReached(); - } - dispatchChanged(ATTACHMENTS_CHANGED); - } - - /** - * Try to add one pending attachment, while guarding against duplicates and - * going over the limit. - * @return true if the attachment limit was reached, false otherwise - */ - private boolean addOnePendingAttachmentNoNotify(final PendingAttachmentData pendingAttachment, - final String bindingId) { - final boolean reachedLimit = getAttachmentCount() >= getAttachmentLimit(); - if (reachedLimit || containsAttachment(pendingAttachment.getContentUri())) { - // Never go over the limit. Never add duplicated attachments. - pendingAttachment.destroyAsync(); - return reachedLimit; - } else { - Assert.isTrue(!mPendingAttachments.contains(pendingAttachment)); - Assert.equals(PendingAttachmentData.STATE_PENDING, pendingAttachment.getCurrentState()); - addAttachment(null /*attachment*/, pendingAttachment); - - pendingAttachment.loadAttachmentForDraft(this, bindingId); - return false; - } - } - - public void setSelfId(final String selfId, final boolean notify) { - LogUtil.d(LogUtil.BUGLE_TAG, "DraftMessageData: set selfId=" + selfId - + " for conversationId=" + mConversationId); - mSelfId = selfId; - if (notify) { - dispatchChanged(SELF_CHANGED); - } - } - - public boolean hasAttachments() { - return !mAttachments.isEmpty(); - } - - public boolean hasPendingAttachments() { - return !mPendingAttachments.isEmpty(); - } - - private int getAttachmentCount() { - return mAttachments.size() + mPendingAttachments.size(); - } - - private int getVideoAttachmentCount() { - int count = 0; - for (MessagePartData part : mAttachments) { - if (part.isVideo()) { - count++; - } - } - for (MessagePartData part : mPendingAttachments) { - if (part.isVideo()) { - count++; - } - } - return count; - } - - private int getAttachmentLimit() { - return BugleGservices.get().getInt( - BugleGservicesKeys.MMS_ATTACHMENT_LIMIT, - BugleGservicesKeys.MMS_ATTACHMENT_LIMIT_DEFAULT); - } - - public void removeAttachment(final MessagePartData attachment) { - for (final MessagePartData existingAttachment : mAttachments) { - if (existingAttachment.getContentUri().equals(attachment.getContentUri())) { - mAttachments.remove(existingAttachment); - existingAttachment.destroyAsync(); - dispatchChanged(ATTACHMENTS_CHANGED); - break; - } - } - } - - public void removeExistingAttachments(final Set<MessagePartData> attachmentsToRemove) { - boolean removed = false; - final Iterator<MessagePartData> iterator = mAttachments.iterator(); - while (iterator.hasNext()) { - final MessagePartData existingAttachment = iterator.next(); - if (attachmentsToRemove.contains(existingAttachment)) { - iterator.remove(); - existingAttachment.destroyAsync(); - removed = true; - } - } - - if (removed) { - dispatchChanged(ATTACHMENTS_CHANGED); - } - } - - public void removePendingAttachment(final PendingAttachmentData pendingAttachment) { - for (final PendingAttachmentData existingAttachment : mPendingAttachments) { - if (existingAttachment.getContentUri().equals(pendingAttachment.getContentUri())) { - mPendingAttachments.remove(pendingAttachment); - pendingAttachment.destroyAsync(); - dispatchChanged(ATTACHMENTS_CHANGED); - break; - } - } - } - - public void updatePendingAttachment(final MessagePartData updatedAttachment, - final PendingAttachmentData pendingAttachment) { - for (final PendingAttachmentData existingAttachment : mPendingAttachments) { - if (existingAttachment.getContentUri().equals(pendingAttachment.getContentUri())) { - mPendingAttachments.remove(pendingAttachment); - if (pendingAttachment.isSinglePartOnly()) { - updatedAttachment.setSinglePartOnly(true); - } - mAttachments.add(updatedAttachment); - dispatchChanged(ATTACHMENTS_CHANGED); - return; - } - } - - // If we are here, this means the pending attachment has been dropped before the task - // to load it was completed. In this case destroy the temporarily staged file since it - // is no longer needed. - updatedAttachment.destroyAsync(); - } - - /** - * Remove the attachments from the draft and notify any listeners. - * @param flags typically this will be ATTACHMENTS_CHANGED. When attachments are cleared in a - * widget, flags will also contain WIDGET_CHANGED. - */ - public void clearAttachments(final int flags) { - destroyAttachments(); - dispatchChanged(flags); - } - - public List<MessagePartData> getReadOnlyAttachments() { - return mReadOnlyAttachments; - } - - public List<PendingAttachmentData> getReadOnlyPendingAttachments() { - return mReadOnlyPendingAttachments; - } - - public boolean loadFromStorage(final BindingBase<DraftMessageData> binding, - final MessageData optionalIncomingDraft, boolean clearLocalDraft) { - LogUtil.d(LogUtil.BUGLE_TAG, "DraftMessageData: " - + (optionalIncomingDraft == null ? "loading" : "setting") - + " for conversationId=" + mConversationId); - if (clearLocalDraft) { - clearLocalDraftCopy(); - } - final boolean isDraftCachedCopy = mIsDraftCachedCopy; - mIsDraftCachedCopy = false; - // Before reading message from db ensure the caller is bound to us (and knows the id) - if (mMonitor == null && !isDraftCachedCopy && isBound(binding.getBindingId())) { - mMonitor = ReadDraftDataAction.readDraftData(mConversationId, - optionalIncomingDraft, binding.getBindingId(), this); - return true; - } - return false; - } - - /** - * Saves the current draft to db. This will save the draft and drop any pending attachments - * we have. The UI typically goes into the background when this is called, and instead of - * trying to persist the state of the pending attachments (the app may be killed, the activity - * may be destroyed), we simply drop the pending attachments for consistency. - */ - public void saveToStorage(final BindingBase<DraftMessageData> binding) { - saveToStorageInternal(binding); - dropPendingAttachments(); - } - - private void saveToStorageInternal(final BindingBase<DraftMessageData> binding) { - // Create MessageData to store to db, but don't clear the in-memory copy so UI will - // continue to display it. - // If self id is null then we'll not attempt to change the conversation's self id. - final MessageData message = createMessageWithCurrentAttachments(false /* clearLocalCopy */); - // Before writing message to db ensure the caller is bound to us (and knows the id) - if (isBound(binding.getBindingId())){ - WriteDraftMessageAction.writeDraftMessage(mConversationId, message); - } - } - - /** - * Called when we are ready to send the message. This will assemble/return the MessageData for - * sending and clear the local draft data, both from memory and from DB. This will also bind - * the message data with a self Id through which the message will be sent. - * - * @param binding the binding object from our consumer. We need to make sure we are still bound - * to that binding before saving to storage. - */ - public MessageData prepareMessageForSending(final BindingBase<DraftMessageData> binding) { - // We can't send the message while there's still stuff pending. - Assert.isTrue(!hasPendingAttachments()); - mSending = true; - // Assembles the message to send and empty working draft data. - // If self id is null then message is sent with conversation's self id. - final MessageData messageToSend = - createMessageWithCurrentAttachments(true /* clearLocalCopy */); - // Note sending message will empty the draft data in DB. - mSending = false; - return messageToSend; - } - - public boolean isSending() { - return mSending; - } - - @Override // ReadDraftMessageActionListener.onReadDraftMessageSucceeded - public void onReadDraftDataSucceeded(final ReadDraftDataAction action, final Object data, - final MessageData message, final ConversationListItemData conversation) { - final String bindingId = (String) data; - - // Before passing draft message on to ui ensure the data is bound to the same bindingid - if (isBound(bindingId)) { - mSelfId = message.getSelfId(); - mIsGroupConversation = conversation.getIsGroup(); - mIncludeEmailAddress = conversation.getIncludeEmailAddress(); - updateFromMessageData(message, bindingId); - LogUtil.d(LogUtil.BUGLE_TAG, "DraftMessageData: draft loaded. " - + "conversationId=" + mConversationId + " selfId=" + mSelfId); - } else { - LogUtil.w(LogUtil.BUGLE_TAG, "DraftMessageData: draft loaded but not bound. " - + "conversationId=" + mConversationId); - } - mMonitor = null; - } - - @Override // ReadDraftMessageActionListener.onReadDraftDataFailed - public void onReadDraftDataFailed(final ReadDraftDataAction action, final Object data) { - LogUtil.w(LogUtil.BUGLE_TAG, "DraftMessageData: draft not loaded. " - + "conversationId=" + mConversationId); - // The draft is now synced with actual MessageData and no longer a cached copy. - mIsDraftCachedCopy = false; - // Just clear the monitor - no update to draft data - mMonitor = null; - } - - /** - * Check if Bugle is default sms app - * @return - */ - public boolean getIsDefaultSmsApp() { - return PhoneUtils.getDefault().isDefaultSmsApp(); - } - - @Override //BindableData.unregisterListeners - protected void unregisterListeners() { - if (mMonitor != null) { - mMonitor.unregister(); - } - mMonitor = null; - mListeners.clear(); - } - - private void destroyAttachments() { - for (final MessagePartData attachment : mAttachments) { - attachment.destroyAsync(); - } - mAttachments.clear(); - mPendingAttachments.clear(); - } - - private void dispatchChanged(final int changeFlags) { - // No change is expected to be made to the draft if it is in cached copy state. - if (mIsDraftCachedCopy) { - return; - } - // Any change in the draft will cancel any pending draft checking task, since the - // size/status of the draft may have changed. - if (mCheckDraftForSendTask != null) { - mCheckDraftForSendTask.cancel(true /* mayInterruptIfRunning */); - mCheckDraftForSendTask = null; - } - mListeners.onDraftChanged(this, changeFlags); - } - - private void dispatchAttachmentLimitReached() { - mListeners.onDraftAttachmentLimitReached(this); - } - - /** - * Drop any pending attachments that haven't finished. This is called after the UI goes to - * the background and we persist the draft data to the database. - */ - private void dropPendingAttachments() { - mPendingAttachments.clear(); - } - - private boolean isDraftEmpty() { - return TextUtils.isEmpty(mMessageText) && mAttachments.isEmpty() && - TextUtils.isEmpty(mMessageSubject); - } - - public boolean isCheckingDraft() { - return mCheckDraftForSendTask != null && !mCheckDraftForSendTask.isCancelled(); - } - - public void checkDraftForAction(final boolean checkMessageSize, final int selfSubId, - final CheckDraftTaskCallback callback, final Binding<DraftMessageData> binding) { - new CheckDraftForSendTask(checkMessageSize, selfSubId, callback, binding) - .executeOnThreadPool((Void) null); - } - - /** - * Allows us to have multiple data listeners for DraftMessageData - */ - private class DraftMessageDataEventDispatcher - extends ArrayList<DraftMessageDataListener> - implements DraftMessageDataListener { - - @Override - @RunsOnMainThread - public void onDraftChanged(DraftMessageData data, int changeFlags) { - Assert.isMainThread(); - for (final DraftMessageDataListener listener : this) { - listener.onDraftChanged(data, changeFlags); - } - } - - @Override - @RunsOnMainThread - public void onDraftAttachmentLimitReached(DraftMessageData data) { - Assert.isMainThread(); - for (final DraftMessageDataListener listener : this) { - listener.onDraftAttachmentLimitReached(data); - } - } - - @Override - @RunsOnMainThread - public void onDraftAttachmentLoadFailed() { - Assert.isMainThread(); - for (final DraftMessageDataListener listener : this) { - listener.onDraftAttachmentLoadFailed(); - } - } - } - - public interface CheckDraftTaskCallback { - void onDraftChecked(DraftMessageData data, int result); - } - - public class CheckDraftForSendTask extends SafeAsyncTask<Void, Void, Integer> { - public static final int RESULT_PASSED = 0; - public static final int RESULT_HAS_PENDING_ATTACHMENTS = 1; - public static final int RESULT_NO_SELF_PHONE_NUMBER_IN_GROUP_MMS = 2; - public static final int RESULT_MESSAGE_OVER_LIMIT = 3; - public static final int RESULT_VIDEO_ATTACHMENT_LIMIT_EXCEEDED = 4; - public static final int RESULT_SIM_NOT_READY = 5; - private final boolean mCheckMessageSize; - private final int mSelfSubId; - private final CheckDraftTaskCallback mCallback; - private final String mBindingId; - private final List<MessagePartData> mAttachmentsCopy; - private int mPreExecuteResult = RESULT_PASSED; - - public CheckDraftForSendTask(final boolean checkMessageSize, final int selfSubId, - final CheckDraftTaskCallback callback, final Binding<DraftMessageData> binding) { - mCheckMessageSize = checkMessageSize; - mSelfSubId = selfSubId; - mCallback = callback; - mBindingId = binding.getBindingId(); - // Obtain an immutable copy of the attachment list so we can operate on it in the - // background thread. - mAttachmentsCopy = new ArrayList<MessagePartData>(mAttachments); - - mCheckDraftForSendTask = this; - } - - @Override - protected void onPreExecute() { - // Perform checking work that can happen on the main thread. - if (hasPendingAttachments()) { - mPreExecuteResult = RESULT_HAS_PENDING_ATTACHMENTS; - return; - } - if (getIsGroupMmsConversation()) { - try { - if (TextUtils.isEmpty(PhoneUtils.get(mSelfSubId).getSelfRawNumber(true))) { - mPreExecuteResult = RESULT_NO_SELF_PHONE_NUMBER_IN_GROUP_MMS; - return; - } - } catch (IllegalStateException e) { - // This happens when there is no active subscription, e.g. on Nova - // when the phone switches carrier. - mPreExecuteResult = RESULT_SIM_NOT_READY; - return; - } - } - if (getVideoAttachmentCount() > MmsUtils.MAX_VIDEO_ATTACHMENT_COUNT) { - mPreExecuteResult = RESULT_VIDEO_ATTACHMENT_LIMIT_EXCEEDED; - return; - } - } - - @Override - protected Integer doInBackgroundTimed(Void... params) { - if (mPreExecuteResult != RESULT_PASSED) { - return mPreExecuteResult; - } - - if (mCheckMessageSize && getIsMessageOverLimit()) { - return RESULT_MESSAGE_OVER_LIMIT; - } - return RESULT_PASSED; - } - - @Override - protected void onPostExecute(Integer result) { - mCheckDraftForSendTask = null; - // Only call back if we are bound to the original binding. - if (isBound(mBindingId) && !isCancelled()) { - mCallback.onDraftChecked(DraftMessageData.this, result); - } else { - if (!isBound(mBindingId)) { - LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: draft not bound"); - } - if (isCancelled()) { - LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: draft is cancelled"); - } - } - } - - @Override - protected void onCancelled() { - mCheckDraftForSendTask = null; - } - - /** - * 1. Check if the draft message contains too many attachments to send - * 2. Computes the minimum size that this message could be compressed/downsampled/encoded - * before sending and check if it meets the carrier max size for sending. - * @see MessagePartData#getMinimumSizeInBytesForSending() - */ - @DoesNotRunOnMainThread - private boolean getIsMessageOverLimit() { - Assert.isNotMainThread(); - if (mAttachmentsCopy.size() > getAttachmentLimit()) { - return true; - } - - // Aggregate the size from all the attachments. - long totalSize = 0; - for (final MessagePartData attachment : mAttachmentsCopy) { - totalSize += attachment.getMinimumSizeInBytesForSending(); - } - return totalSize > MmsConfig.get(mSelfSubId).getMaxMessageSize(); - } - } - - public void onPendingAttachmentLoadFailed(PendingAttachmentData data) { - mListeners.onDraftAttachmentLoadFailed(); - } -} |