zoukankan      html  css  js  c++  java
  • 深度分析: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));
            }
        }


  • 相关阅读:
    (2015年郑州轻工业学院ACM校赛题) B迷宫
    (2015年郑州轻工业学院ACM校赛题) A 彩票
    POJ 1861 Network
    动态逆序对
    K大数查询
    Dynamic Rankings
    Cleaning
    Boxes
    P3601 签到题
    How many integers can you find
  • 原文地址:https://www.cnblogs.com/bill-technology/p/4130928.html
Copyright © 2011-2022 走看看