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

因为工作需要,再加上个人爱好,经过分析整理出短彩应用中从发送至收到附件为音频的彩信的下载,预览,播放整个流程,给大家一起分享。

第一步,添加附件:ComposeMessageActivity类下,addAttachement();

private void addAttachment(int type, boolean replace) {
        // Calculate the size of the current slide if we're doing a replace so the
        // slide size can optionally be used in computing how much room is left for an attachment.
        int currentSlideSize = 0;
        SlideshowModel slideShow = mWorkingMessage.getSlideshow();
        if (replace && slideShow != null) {
            WorkingMessage.removeThumbnailsFromCache(slideShow);
            SlideModel slide = slideShow.get(0);
            currentSlideSize = slide.getSlideSize();
        }
        switch (type) {
            case AttachmentTypeSelectorAdapter.ADD_IMAGE:
                MessageUtils.selectImage(this, REQUEST_CODE_ATTACH_IMAGE);
                break;

            case AttachmentTypeSelectorAdapter.TAKE_PICTURE: {
                MessageUtils.capturePicture(this, REQUEST_CODE_TAKE_PICTURE);
                break;
            }

            case AttachmentTypeSelectorAdapter.ADD_VIDEO:
                MessageUtils.selectVideo(this, REQUEST_CODE_ATTACH_VIDEO);
                break;

            case AttachmentTypeSelectorAdapter.RECORD_VIDEO: {
                long sizeLimit = computeAttachmentSizeLimit(slideShow, currentSlideSize);
                if (sizeLimit > 0) {
                    MessageUtils.recordVideo(this, REQUEST_CODE_TAKE_VIDEO, sizeLimit);
                } else {
                    Toast.makeText(this,
                            getString(R.string.message_too_big_for_video),
                            Toast.LENGTH_SHORT).show();
                }
            }
            break;

            case AttachmentTypeSelectorAdapter.ADD_SOUND:
                MessageUtils.selectAudio(this, REQUEST_CODE_ATTACH_SOUND);
                break;


            case AttachmentTypeSelectorAdapter.RECORD_SOUND:
                long sizeLimit = computeAttachmentSizeLimit(slideShow, currentSlideSize);
                MessageUtils.recordSound(this, REQUEST_CODE_RECORD_SOUND, sizeLimit);
                break;

            case AttachmentTypeSelectorAdapter.ADD_SLIDESHOW:
                editSlideshow();
                break;

            case AttachmentTypeSelectorAdapter.ADD_CONTACT_AS_TEXT:
                pickContacts(MultiPickContactsActivity.MODE_INFO,
                        replace ? REQUEST_CODE_ATTACH_REPLACE_CONTACT_INFO
                                : REQUEST_CODE_ATTACH_ADD_CONTACT_INFO);
                break;

            case AttachmentTypeSelectorAdapter.ADD_CONTACT_AS_VCARD:
                pickContacts(MultiPickContactsActivity.MODE_VCARD,
                        REQUEST_CODE_ATTACH_ADD_CONTACT_VCARD);
                break;

            default:
                break;
        }
    }

第二步,选择音频类型:MessageUtils类中的selectAudio()方法;

public static void selectAudio(final Activity activity, final int requestCode) {
        // Compare other phone's behavior, we are not only display the
        // RingtonePick to add, we could have other choices like external audio
        // and system audio. Allow the user to select a particular kind of data
        // and return it.
        String[] items = new String[2];
        items[SELECT_SYSTEM] = activity.getString(R.string.system_audio_item);
        items[SELECT_EXTERNAL] = activity.getString(R.string.external_audio_item);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity,
                android.R.layout.simple_list_item_1, android.R.id.text1, items);
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        AlertDialog dialog = builder.setTitle(activity.getString(R.string.select_audio))
                .setAdapter(adapter, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent audioIntent = null;
                        switch (which) {
                            case SELECT_SYSTEM:
                                audioIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);//android.intent.action.RINGTONE_PICKER
                                //add by zhihui.wang for SWBUG00028878 at 2014-5-5.
                                audioIntent.addCategory("android.intent.category.SIMRINGTONE");
                                audioIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
                                audioIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
                                audioIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, false);
                                audioIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE,
                                        activity.getString(R.string.select_audio));
                                audioIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
                                break;
                            case SELECT_EXTERNAL:
                                audioIntent = new Intent();
                                audioIntent.setAction(Intent.ACTION_PICK);
                                audioIntent.setData(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
                                break;
                        }
                        activity.startActivityForResult(audioIntent, requestCode);
                    }
                })
                .create();
        dialog.show();
    }

在上述代码中弹出Choose audio对话框,选项分别为System audio和External audio,我们这里选择System audio选项,这里在Intent中封装了彩铃设置为没有默认选项(EXTRA_RINGTONE_SHOW_DEFAULT),非静音状态(EXTRA_RINGTONE_SHOW_SILENT),非数字版权管理(EXTRA_RINGTONE_INCLUDE_DRM),标题(EXTRA_RINGTONE_TITLE)-Choose audio

第三步,选择音频文件:RingtonePickerActivity类,该类继承了AlertActivity类,选择音频文件,OK;

 public void onClick(DialogInterface dialog, int which) {
        boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;

        // Should't response the "OK" and "Cancel" button's click event at the
        // same time.
        if (mIsHasClick || (mCursor == null)) {
            return;
        }
        mIsHasClick = true;

        // Stop playing the previous ringtone
        mRingtoneManager.stopPreviousRingtone();

        if (positiveResult) {
            Intent resultIntent = new Intent();
            Uri uri = null;

            if (mClickedPos == mDefaultRingtonePos) {
                // Set it to the default Uri that they originally gave us
                uri = mUriForDefaultItem;
            } else if (mClickedPos == mSilentPos) {
                // A null Uri is for the 'Silent' item
                uri = null;
            } else {
                uri = mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(mClickedPos));
            }

            resultIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, uri);//所选音频文件的地址
            setResult(RESULT_OK, resultIntent);//RESULT_OK=-1
        } else {
            setResult(RESULT_CANCELED);
        }

        getWindow().getDecorView().post(new Runnable() {
            public void run() {
                mCursor.deactivate();
            }
        });

        finish();
    }

选择音频,添加附件成功后,返回ComposeMessage,编辑彩信界面,这里我们继续输入文本内容:这里介绍一下与编辑彩信文本内容的控件为ComposeMessageActivity类的mTextEditor;

第四步,处理附件:选择附件后,处理添加的附件;<TAG 1-1>

  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (LogTag.VERBOSE) {
            log("onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode +
                    ", data=" + data);
        }
        mWaitingForSubActivity = false;          // We're back!
        mShouldLoadDraft = false;
        if (mWorkingMessage.isFakeMmsForDraft()) {
            // We no longer have to fake the fact we're an Mms. At this point we are or we aren't,
            // based on attachments and other Mms attrs.
            mWorkingMessage.removeFakeMmsForDraft();
        }

        if (requestCode == REQUEST_CODE_PICK) {
            mWorkingMessage.asyncDeleteDraftSmsMessage(mConversation);
        }

        if (requestCode == REQUEST_CODE_ADD_CONTACT) {
            // The user might have added a new contact. When we tell contacts to add a contact
            // and tap "Done", we're not returned to Messaging. If we back out to return to
            // messaging after adding a contact, the resultCode is RESULT_CANCELED. Therefore,
            // assume a contact was added and get the contact and force our cached contact to
            // get reloaded with the new info (such as contact name). After the
            // contact is reloaded, the function onUpdate() in this file will get called
            // and it will update the title bar, etc.
            if (mAddContactIntent != null) {
                String address =
                    mAddContactIntent.getStringExtra(ContactsContract.Intents.Insert.EMAIL);
                if (address == null) {
                    address =
                        mAddContactIntent.getStringExtra(ContactsContract.Intents.Insert.PHONE);
                }
                if (address != null) {
                    Contact contact = Contact.get(address, false);
                    if (contact != null) {
                        contact.reload();
                    }
                }
            }
        }

        if (requestCode == AttachmentEditor.MSG_PLAY_AUDIO
                || requestCode == AttachmentEditor.MSG_PLAY_SLIDESHOW
                || requestCode == AttachmentEditor.MSG_PLAY_VIDEO) {
            // When the audio has finished to play, we put the
            // mIsAudioPlayerActivityRunning to false.
            mIsAudioPlayerActivityRunning = false;
        }

        if (resultCode != RESULT_OK){
            if (LogTag.VERBOSE) log("bail due to resultCode=" + resultCode);
            return;
        }

        switch (requestCode) {
            case REQUEST_CODE_CREATE_SLIDESHOW:
                if (data != null) {
                    mAttachFileUri = data.getData();
                    mIsSendMultiple = false;
                    WorkingMessage newMessage = WorkingMessage.load(this, mAttachFileUri);
                    if (newMessage != null) {
                        // Here we should keep the subject from the old mWorkingMessage.
                        setNewMessageSubject(newMessage);
                        mWorkingMessage = newMessage;
                        mWorkingMessage.setConversation(mConversation);
                        updateThreadIdIfRunning();
                        updateMmsSizeIndicator();
                        drawTopPanel(false);
                        drawBottomPanel();
                        updateSendButtonState();
                        updateAttachButtonState();
                    }
                }
                break;

            case REQUEST_CODE_TAKE_PICTURE: {
                // create a file based uri and pass to addImage(). We want to read the JPEG
                // data directly from file (using UriImage) instead of decoding it into a Bitmap,
                // which takes up too much memory and could easily lead to OOM.
                File file = new File(TempFileProvider.getScrapPath(this));
                mAttachFileUri = Uri.fromFile(file);

                // Remove the old captured picture's thumbnail from the cache
                if(MmsApp.getApplication().getThumbnailManager() != null) {
                   MmsApp.getApplication().getThumbnailManager().removeThumbnail(mAttachFileUri);

                   addImageAsync(mAttachFileUri, false);
                }

                break;
            }

            case REQUEST_CODE_ATTACH_IMAGE: {
                if (data != null) {
                    mAttachFileUri = data.getData();
                    addImageAsync(mAttachFileUri, false);
                }
                break;
            }

            case REQUEST_CODE_TAKE_VIDEO:
                mAttachFileUri = TempFileProvider.renameScrapFile(".3gp", null, this);
                // Remove the old captured video's thumbnail from the cache
                MmsApp.getApplication().getThumbnailManager().removeThumbnail(mAttachFileUri);

                addVideoAsync(mAttachFileUri, false);      // can handle null videoUri
                break;

            case REQUEST_CODE_ATTACH_VIDEO:
                if (data != null) {
                    mAttachFileUri = data.getData();
                    addVideoAsync(mAttachFileUri, false);
                }
                break;

            case REQUEST_CODE_ATTACH_SOUND: {//104
                // Attempt to add the audio to the  attachment.
                Uri uri = (Uri) data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
                if (uri == null) {
                    uri = data.getData();
                } else if (Settings.System.DEFAULT_RINGTONE_URI.equals(uri)) {
                    break;
                }
                mAttachFileUri = uri;
                addAudio(mAttachFileUri, false);
                break;
            }


            case REQUEST_CODE_RECORD_SOUND:
                if (data != null) {
                    mAttachFileUri = data.getData();
                    addAudio(mAttachFileUri,false);
                }
                break;

            case REQUEST_CODE_ECM_EXIT_DIALOG:
                boolean outOfEmergencyMode = data.getBooleanExtra(EXIT_ECM_RESULT, false);
                if (outOfEmergencyMode) {
                    sendMessage(false);
                }
                break;

            case REQUEST_CODE_PICK:
                if (data != null) {
                    processPickResult(data);
                }
                break;

            case REQUEST_CODE_ATTACH_REPLACE_CONTACT_INFO:
                // Caused by user choose to replace the attachment, so we need remove
                // the attachment and then add the contact info to text.
                if (data != null) {
                    mWorkingMessage.removeAttachment(true);
                    mAttachFileUri = null;
                }
            case REQUEST_CODE_ATTACH_ADD_CONTACT_INFO:
                if (data != null) {
                    String newText = mWorkingMessage.getText() +
                        data.getStringExtra(MultiPickContactsActivity.EXTRA_INFO);
                    mWorkingMessage.setText(newText);
                }
                break;

            case REQUEST_CODE_ATTACH_ADD_CONTACT_VCARD:
                if (data != null) {
                    // In a case that a draft message has an attachment whose type is slideshow,
                    // then reopen it and replace the attachment through attach icon, we have to
                    // remove the old attachement silently first.
                    if (mWorkingMessage != null) {
                        mWorkingMessage.removeAttachment(false);
                        mAttachFileUri = null;
                    }
                    String extraVCard = data.getStringExtra(MultiPickContactsActivity.EXTRA_VCARD);
                    if (extraVCard != null) {
                        Uri vcard = Uri.parse(extraVCard);
                        addVcard(vcard);
                    }
                }
                break;

            default:
                if (LogTag.VERBOSE) log("bail due to unknown requestCode=" + requestCode);
                break;
        }
    }

上述代码调用了addAudio;<TAG 1-2>

    private void addAudio(Uri uri, boolean append) {
        if (uri != null) {
            int result = mWorkingMessage.setAttachment(WorkingMessage.AUDIO, uri, append);
            handleAddAttachmentError(result, R.string.type_audio);

        }
    }

上述代码<TAG 1-2-1>中WorkingMessage类(信息发送的第一站,它会先处理一下信息的相关内容,比如刷新收信人(Sync Recipients)以保证都是合法收信人,把附件(Slideshow)转成可发送的彩信附件Pdu(SendReq),makeSendReq。然后针对,不同的信息类型(短信,彩信)调用不同的处理类来处理。处理的流程也比较类似,都是先把消息放到一个队列中,然后启动相应的Service来处理。Service会维护信息队列,然后处理每个信息。短信是由Frameworks中的SmsManager发送出去,而彩信是通过Http协议发送。)中调用了setAttachment()方法;<TAG 1-3>

 public int setAttachment(int type, Uri dataUri, boolean append) {
        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
            LogTag.debug("setAttachment type=%d uri %s", type, dataUri);
        }
        int result = OK;
        SlideshowEditor slideShowEditor = new SlideshowEditor(mActivity, mSlideshow);//创建一个幻灯片编辑器实例

        // Special case for deleting a slideshow. When ComposeMessageActivity gets told to
        // remove an attachment (search for AttachmentEditor.MSG_REMOVE_ATTACHMENT), it calls
        // this function setAttachment with a type of TEXT and a null uri. Basically, it's turning
        // the working message from an MMS back to a simple SMS. The various attachment types
        // use slide[0] as a special case. The call to ensureSlideshow below makes sure there's
        // a slide zero. In the case of an already attached slideshow, ensureSlideshow will do
        // nothing and the slideshow will remain such that if a user adds a slideshow again, they'll
        // see their old slideshow they previously deleted. Here we really delete the slideshow.
        if (type == TEXT && mAttachmentType == SLIDESHOW && mSlideshow != null && dataUri == null
                && !append) {
            slideShowEditor.removeAllSlides();
        }

        // Make sure mSlideshow is set up and has a slide.
        ensureSlideshow();      // mSlideshow can be null before this call, won't be afterwards//确认创建一个幻灯片实例,如果没有这里会进行处理
        slideShowEditor.setSlideshow(mSlideshow);

        // Change the attachment
        result = append ? appendMedia(type, dataUri, slideShowEditor)
                : changeMedia(type, dataUri, slideShowEditor);

        // If we were successful, update mAttachmentType and notify
        // the listener than there was a change.
        if (result == OK) {
            mAttachmentType = type;
        }
        correctAttachmentState();   // this can remove the slideshow if there are no attachments

        if (mSlideshow != null && type == IMAGE) {
            // Prime the image's cache; helps A LOT when the image is coming from the network
            // (e.g. Picasa album). See b/5445690.
            int numSlides = mSlideshow.size();
            if (numSlides > 0) {
                ImageModel imgModel = mSlideshow.get(numSlides - 1).getImage();
                if (imgModel != null) {
                    cancelThumbnailLoading();
                    imgModel.loadThumbnailBitmap(null);
                }
            }
        }

        mStatusListener.onAttachmentChanged();  // have to call whether succeeded or failed,
                                                // because a replace that fails, removes the slide

        if (!append && mAttachmentType == TEXT && type == TEXT) {
            int[] params = SmsMessage.calculateLength(getText(), false);
            /* SmsMessage.calculateLength returns an int[4] with:
             *   int[0] being the number of SMS's required,
             *   int[1] the number of code units used,
             *   int[2] is the number of code units remaining until the next message.
             *   int[3] is the encoding type that should be used for the message.
             */
            int smsSegmentCount = params[0];

            if (!MmsConfig.getMultipartSmsEnabled()) {
                // The provider doesn't support multi-part sms's so as soon as the user types
                // an sms longer than one segment, we have to turn the message into an mms.
                setLengthRequiresMms(smsSegmentCount > 1, false);
            } else {
                int threshold = MmsConfig.getSmsToMmsTextThreshold();
                setLengthRequiresMms(threshold > 0 && smsSegmentCount > threshold, false);
            }
        } else {
            // Set HAS_ATTACHMENT if we need it.
            updateState(HAS_ATTACHMENT, hasAttachment(), true);
        }
        return result;
    }
上述代码<TAG 1-3>由于这里主要研究的附件主要是音频附件,因此我们直接调用了updateState()方法;

    private void updateState(int state, boolean on, boolean notify) {
        if (!sMmsEnabled) {
            // If Mms isn't enabled, the rest of the Messaging UI should not be using any
            // feature that would cause us to to turn on any Mms flag and show the
            // "Converting to multimedia..." message.
            return;
        }
        int oldState = mMmsState;
        if (on) {
            mMmsState |= state;
        } else {
            mMmsState &= ~state;
        }

        // If we are clearing the last bit that is not FORCE_MMS,
        // expire the FORCE_MMS bit.
        if (mMmsState == FORCE_MMS && ((oldState & ~FORCE_MMS) > 0)) {
            mMmsState = 0;
        }

        // Notify the listener if we are moving from SMS to MMS
        // or vice versa.//这里判断状态,并弹出短信切换至彩信对话框或彩信切换之短信对话框
        if (notify) {
            if (oldState == 0 && mMmsState != 0) {
                mStatusListener.onProtocolChanged(true);
            } else if (oldState != 0 && mMmsState == 0) {
                mStatusListener.onProtocolChanged(false);
            }
        }

        if (oldState != mMmsState) {
            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) LogTag.debug("updateState: %s%s = %s",
                    on ? "+" : "-",
                    stateString(state), stateString(mMmsState));
        }
    }


原文地址:https://www.cnblogs.com/bill-technology/p/4130928.html