• 深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(二,发送彩信<1>)


    当准备工作(添加附件,输入文本内容)完成之后,我们这里开始进行该流程分析的第二阶段,也就是发送彩信。这里我们从ComposeMessageActivity类的点击发送按钮(mSendButtonMms)的点击事件开始:<TAG 1-1>

        @Override
        public void onClick(View v) {
            if (mShowTwoButtons && (v == mSendButtonSmsViewSec || v == mSendButtonMmsViewSec)
                    && isPreparedForSending()) {
                confirmSendMessageIfNeeded(MSimConstants.SUB2);
            } else if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) {
                if (mShowTwoButtons) {
                    confirmSendMessageIfNeeded(MSimConstants.SUB1);
                } else {
                    confirmSendMessageIfNeeded();
                }
            } else if ((v == mRecipientsPicker)) {
                launchMultiplePhonePicker();
            } else if ((v == mRecipientsPickerGroups)) {
                launchContactGroupPicker();
            } else if (v == mAttachButton) {
                showAddAttachmentDialog(false);
            }
        }

    上述<TAG 1-1>调用了confirmSendMessageIfNeeded方法,这里对收件人(mRecipientEditor)进行了一些处理;<TAG 1-2>

        private void confirmSendMessageIfNeeded() {
            boolean isMms = mWorkingMessage.requiresMms();
            if (!isRecipientsEditorVisible()) {
                if (MessageUtils.isMobileDataDisabled(this) &&
                        MessageUtils.CAN_SETUP_MMS_DATA && isMms) {
                    showMobileDataDisabledDialog();
                } else if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                    sendMsimMessage(true);
                } else {
                    sendMessage(true);
                }
                return;
            }

            if (mRecipientsEditor.hasInvalidRecipient(isMms)) {
                showInvalidRecipientDialog();
            } else if (MessageUtils.isMobileDataDisabled(this) &&//判断数据连接是否正常
                    MessageUtils.CAN_SETUP_MMS_DATA && isMms) {
                showMobileDataDisabledDialog();
            } else {
                // The recipients editor is still open. Make sure we use what's showing there
                // as the destination.
                ContactList contacts = mRecipientsEditor.constructContactsFromInput(false);
                mDebugRecipients = contacts.serialize();
                if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                    sendMsimMessage(true);
                } else {
                    sendMessage(true);
                }
            }
        }
    上述代码<TAG 1-2>中,调用了ComposeMessageActivity类的sendMessage方法;<TAG 1-3>
        private void sendMessage(boolean bCheckEcmMode) {
            // Check message size, if >= max message size, do not send message.
            if(checkMessageSizeExceeded()){
                return;
            }

            // If message is sent make the mIsMessageChanged is true
            // when activity is from SearchActivity.
            mIsMessageChanged = mIsFromSearchActivity;
            if (bCheckEcmMode) {
                // TODO: expose this in telephony layer for SDK build
                String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE);
                if (Boolean.parseBoolean(inEcm)) {
                    try {
                        startActivityForResult(
                                new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null),
                                REQUEST_CODE_ECM_EXIT_DIALOG);
                        return;
                    } catch (ActivityNotFoundException e) {
                        // continue to send message
                        Log.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e);
                    }
                }
            }

            if (!mSendingMessage) {
                if (LogTag.SEVERE_WARNING) {
                    String sendingRecipients = mConversation.getRecipients().serialize();
                    if (!sendingRecipients.equals(mDebugRecipients)) {
                        String workingRecipients = mWorkingMessage.getWorkingRecipients();
                        if (!mDebugRecipients.equals(workingRecipients)) {
                            LogTag.warnPossibleRecipientMismatch("ComposeMessageActivity.sendMessage" +
                                    " recipients in window: "" +
                                    mDebugRecipients + "" differ from recipients from conv: "" +
                                    sendingRecipients + "" and working recipients: " +
                                    workingRecipients, this);
                        }
                    }
                    sanityCheckConversation();
                }

                // send can change the recipients. Make sure we remove the listeners first and then add
                // them back once the recipient list has settled.
                removeRecipientsListeners();


                if (mWorkingMessage.getResendMultiRecipients()) {
                    //if resend sms recipient is more than one, use mResendSmsRecipient
                    mWorkingMessage.send(mResendSmsRecipient);
                } else {
                    mWorkingMessage.send(mDebugRecipients);
                }

                mSentMessage = true;
                mSendingMessage = true;
                addRecipientsListeners();

                mScrollOnSend = true;   // in the next onQueryComplete, scroll the list to the end.
            }
            // But bail out if we are supposed to exit after the message is sent.
            if (mSendDiscreetMode) {
                finish();
            }
        }

    上述代码<TAG 1-3>中,调用了WorkingMessage类中的send()方法;<TAG 1-4>
     public void send(final String recipientsInUI) {

            long origThreadId = mConversation.getThreadId();

            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
                LogTag.debug("send origThreadId: " + origThreadId);
            }

            removeSubjectIfEmpty(true /* notify */);//这里判断当前彩信中的主题是否为空,如果为空则移除主题

            // Get ready to write to disk.
            prepareForSave(true /* notify */);

            // We need the recipient list for both SMS and MMS.
            final Conversation conv = mConversation;
            String msgTxt = mText.toString();

            if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
                // uaProfUrl setting in mms_config.xml must be present to send an MMS.
                // However, SMS service will still work in the absence of a uaProfUrl address.
                if (MmsConfig.getUaProfUrl() == null) {
                    String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " +
                            "missing uaProfUrl setting.  uaProfUrl is required for MMS service, " +
                            "but can be absent for SMS.";
                    RuntimeException ex = new NullPointerException(err);
                    Log.e(TAG, err, ex);
                    // now, let's just crash.
                    throw ex;
                }

                // Make local copies of the bits we need for sending a message,
                // because we will be doing it off of the main thread, which will
                // immediately continue on to resetting some of this state.
                final Uri mmsUri = mMessageUri;
                final PduPersister persister = PduPersister.getPduPersister(mActivity);

                final SlideshowModel slideshow = mSlideshow;
                final CharSequence subject = mSubject;
                final boolean textOnly = mAttachmentType == TEXT;

                if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
                    LogTag.debug("Send mmsUri: " + mmsUri);
                }

                // Do the dirty work of sending the message off of the main UI thread.
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        final SendReq sendReq = makeSendReq(conv, subject);

                        // Make sure the text in slide 0 is no longer holding onto a reference to
                        // the text in the message text box.
                        slideshow.prepareForSend();
                        sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly);

                        updateSendStats(conv);
                    }
                }, "WorkingMessage.send MMS").start();
            } else {
                // Same rules apply as above.
                // add user's signature first if this feature is enabled.
                String text = mText.toString();
                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity);
                if (sp.getBoolean("pref_key_enable_signature", false)) {
                    String signature = (sp.getString("pref_key_edit_signature", "")).trim();
                    if (signature.length() > 0) {
                        String sigBlock = " " + signature;
                        if (!text.endsWith(sigBlock)) {
                            // Signature should be written behind the text in a
                            // newline while the signature has changed.
                            text += sigBlock;
                        }
                    }
                }
                final String msgText = text;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        preSendSmsWorker(conv, msgText, recipientsInUI);

                        updateSendStats(conv);
                    }
                }, "WorkingMessage.send SMS").start();
            }

            // update the Recipient cache with the new to address, if it's different
            RecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients());

            // Mark the message as discarded because it is "off the market" after being sent.
            mDiscarded = true;
        }

    上述代码<TAG 1-4>中,生成了彩信的可发送的Pdu—SendReq,接着调用了sendMmsWorker()接着会把彩信写入数据库,把要发送的SendReq也会写入数据库,后面会再从数据库中读取出SendReq,并标识为草稿;然后会构建MmsMessageSender,传入收信人和彩信的Uri,让其发送。这期间也会回调UI一次,以初始化收信人编辑框和信息编辑框<TAG 1-5>

    private void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister,
                SlideshowModel slideshow, SendReq sendReq, boolean textOnly) {
            long threadId = 0;
            Cursor cursor = null;
            boolean newMessage = false;
            boolean forwardMessage = conv.getHasMmsForward();
            boolean sameRecipient = false;
            ContactList contactList = conv.getRecipients();
            if (contactList != null) {
                String[] numbers = contactList.getNumbers();
                String[] forward = conv.getForwardRecipientNumber();
                if (numbers != null && forward != null
                        && (numbers.length == forward.length)) {
                    List<String> currentNumberList = Arrays.asList(numbers);
                    List<String> forwardNumberList = Arrays.asList(forward);
                    Collections.sort(currentNumberList);
                    Collections.sort(forwardNumberList);
                    if (currentNumberList.equals(forwardNumberList)) {
                        sameRecipient = true;
                    }
                }
            }
            try {
                // Put a placeholder message in the database first
                DraftCache.getInstance().setSavingDraft(true);
                mStatusListener.onPreMessageSent();

                // Make sure we are still using the correct thread ID for our
                // recipient set.
                threadId = conv.ensureThreadId();
                if (forwardMessage && sameRecipient) {
                    MessageUtils.sSameRecipientList.add(threadId);
                }

                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                    LogTag.debug("sendMmsWorker: update draft MMS message " + mmsUri +
                            " threadId: " + threadId);
                }

                // One last check to verify the address of the recipient.
                String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);
                if (dests.length == 1) {
                    // verify the single address matches what's in the database. If we get a different
                    // address back, jam the new value back into the SendReq.
                    String newAddress =
                        Conversation.verifySingleRecipient(mActivity, conv.getThreadId(), dests[0]);

                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                        LogTag.debug("sendMmsWorker: newAddress " + newAddress +
                                " dests[0]: " + dests[0]);
                    }

                    if (!newAddress.equals(dests[0])) {
                        dests[0] = newAddress;
                        EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);
                        if (encodedNumbers != null) {
                            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                                LogTag.debug("sendMmsWorker: REPLACING number!!!");
                            }
                            sendReq.setTo(encodedNumbers);
                        }
                    }
                }
                newMessage = mmsUri == null;
                if (newMessage) {
                    // Write something in the database so the new message will appear as sending
                    ContentValues values = new ContentValues();
                    values.put(Mms.MESSAGE_BOX, Mms.MESSAGE_BOX_OUTBOX);
                    values.put(Mms.THREAD_ID, threadId);
                    values.put(Mms.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
                    if (textOnly) {
                        values.put(Mms.TEXT_ONLY, 1);
                    }
                    mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI,
                            values);
                }
                mStatusListener.onMessageSent();

                // If user tries to send the message, it's a signal the inputted text is
                // what they wanted.
                UserHappinessSignals.userAcceptedImeText(mActivity);

                // First make sure we don't have too many outstanding unsent message.
                cursor = SqliteWrapper.query(mActivity, mContentResolver,
                        Mms.Outbox.CONTENT_URI, MMS_OUTBOX_PROJECTION, null, null, null);
                if (cursor != null) {
                    long maxMessageSize = MmsConfig.getMaxSizeScaleForPendingMmsAllowed() *
                    MmsConfig.getMaxMessageSize();
                    long totalPendingSize = 0;
                    while (cursor.moveToNext()) {
                        totalPendingSize += cursor.getLong(MMS_MESSAGE_SIZE_INDEX);
                    }
                    if (totalPendingSize >= maxMessageSize) {
                        unDiscard();    // it wasn't successfully sent. Allow it to be saved as a draft.
                        mStatusListener.onMaxPendingMessagesReached();
                        markMmsMessageWithError(mmsUri);
                        return;
                    }
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }

            try {
                if (newMessage) {
                    // Create a new MMS message if one hasn't been made yet.
                    mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri,
                            mActivity, null);
                } else {
                    // Otherwise, sync the MMS message in progress to disk.
                    updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);
                }

                // Be paranoid and clean any draft SMS up.
                deleteDraftSmsMessage(threadId);
            } finally {
                DraftCache.getInstance().setSavingDraft(false);
            }

            // Resize all the resizeable attachments (e.g. pictures) to fit
            // in the remaining space in the slideshow.
            /*int error = 0;
            try {
                slideshow.finalResize(mmsUri);
            } catch (ExceedMessageSizeException e1) {
                error = MESSAGE_SIZE_EXCEEDED;
            } catch (MmsException e1) {
                error = UNKNOWN_ERROR;
            }
            if (error != 0) {
                markMmsMessageWithError(mmsUri);
                mStatusListener.onAttachmentError(error);
                return;
            }*/

            ContentValues values = new ContentValues(1);
            if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {
                values.put(Mms.SUB_ID, mCurrentConvSub);
            } else {
               values.put(Mms.SUB_ID, MSimTelephonyManager.getDefault().getPreferredDataSubscription());
            }
            SqliteWrapper.update(mActivity, mContentResolver, mmsUri, values, null, null);

            MessageSender sender = new MmsMessageSender(mActivity, mmsUri,
                    slideshow.getCurrentMessageSize(), mCurrentConvSub);
            try {
                if (!sender.sendMessage(threadId)) {
                    // The message was sent through SMS protocol, we should
                    // delete the copy which was previously saved in MMS drafts.
                    SqliteWrapper.delete(mActivity, mContentResolver, mmsUri, null, null);
                }
                // After send mms, this thread should't have draft left.
                // Be paranoid and clean any draft MMS up.
                deleteDraftMmsMessage(threadId);

                // Make sure this thread isn't over the limits in message count
                Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
            } catch (Exception e) {
                Log.e(TAG, "Failed to send message: " + mmsUri + ", threadId=" + threadId, e);
            }
            if (forwardMessage && sameRecipient) {
                MessageUtils.sSameRecipientList.remove(threadId);
            }
            MmsWidgetProvider.notifyDatasetChanged(mActivity);
            updateSearchResult();
        }

    上述代码<TAG 1-5>中,创建MmsMessageSender实例并调用了sendMessage()方法;MmsMessageSender先从数据库中读出彩信发送的Pdu—SendReq,Google的内置包com.google.android.mms.*;里面封装了所有操作Pdu的方法,包括把Pdu写入数据库(PduPersister.persist()),从数据库中读取生成Pdu(PduPersister.load())。然后根据当前彩信的配置和其他信息对SendReq进行更新,比如设置Expiration,Priority,Date和Size等,把彩信移到Outbox,然后启动TransactionService来处理彩信。sendMessage()就此返回。WorkingMessage会再次回调UI的接口,因为此时彩信已被在数据库中,所以UI会刷新信息列表,显示刚刚的彩信,这时的状态应该是正在发送中。

        public boolean sendMessage(long token) throws MmsException {
            // Load the MMS from the message uri
            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
                LogTag.debug("sendMessage uri: " + mMessageUri);
            }
            PduPersister p = PduPersister.getPduPersister(mContext);
            GenericPdu pdu = p.load(mMessageUri);

            if (pdu.getMessageType() != PduHeaders.MESSAGE_TYPE_SEND_REQ) {
                throw new MmsException("Invalid message: " + pdu.getMessageType());
            }

            SendReq sendReq = (SendReq) pdu;

            // Update headers.
            updatePreferencesHeaders(sendReq);

            // MessageClass.
            sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes());

            // Update the 'date' field of the message before sending it.
            sendReq.setDate(System.currentTimeMillis() / 1000L);

            sendReq.setMessageSize(mMessageSize);

            p.updateHeaders(mMessageUri, sendReq);

            long messageId = ContentUris.parseId(mMessageUri);

            // Move the message into MMS Outbox.
            if (!mMessageUri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) {
                // If the message is already in the outbox (most likely because we created a "primed"
                // message in the outbox when the user hit send), then we have to manually put an
                // entry in the pending_msgs table which is where TransacationService looks for
                // messages to send. Normally, the entry in pending_msgs is created by the trigger:
                // insert_mms_pending_on_update, when a message is moved from drafts to the outbox.
                ContentValues values = new ContentValues(7);

                values.put(PendingMessages.PROTO_TYPE, MmsSms.MMS_PROTO);
                values.put(PendingMessages.MSG_ID, messageId);
                values.put(PendingMessages.MSG_TYPE, pdu.getMessageType());
                values.put(PendingMessages.ERROR_TYPE, 0);
                values.put(PendingMessages.ERROR_CODE, 0);
                values.put(PendingMessages.RETRY_INDEX, 0);
                values.put(PendingMessages.DUE_TIME, 0);

                SqliteWrapper.insert(mContext, mContext.getContentResolver(),
                        PendingMessages.CONTENT_URI, values);
            } else {
                p.move(mMessageUri, Mms.Outbox.CONTENT_URI);
            }

            // Start MMS transaction service
            SendingProgressTokenManager.put(messageId, token);
            Intent intent = new Intent(mContext, TransactionService.class);
            intent.putExtra(Mms.SUB_ID, mSubscription); //destination sub id
            intent.putExtra(MultiSimUtility.ORIGIN_SUB_ID,
                    MultiSimUtility.getCurrentDataSubscription(mContext));
            if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {

                Intent silentIntent = new Intent(mContext,
                        com.android.mms.ui.SelectMmsSubscription.class);
                silentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                silentIntent.putExtras(intent); //copy all extras
                mContext.startService(silentIntent);
            } else {
                mContext.startService(intent);
            }

            return true;
        }










  • 相关阅读:
    函数
    2017-12-09 JavaScript实现ZLOGO子集: 测试用例
    2017-12-06 JavaScript实现ZLOGO子集: 单层循环功能
    2017-12-05 JavaScript实现ZLOGO子集: 前进+转向
    Python3选择支持非ASCII码标识符的缘由
    2017-12-04 编写Visual Studio Code插件初尝试
    2017-12-02 编程语言试验之Antlr4+JavaScript实现"圈4"
    2017-12-01 中英文代码对比之ZLOGO 4 & LOGO
    2017-11-28 在线编程网站对中文代码的支持
    中文编程兴起的可能途径
  • 原文地址:https://www.cnblogs.com/bill-technology/p/4130923.html
Copyright © 2020-2023  润新知