Android4.2
1. MediaModel 代表一项附件。实现接口EventListener。
2. SlideModel 代表一页幻灯片。实现接口List<MediaModel>, EventListener
3. SlideshowModel 代表所有幻灯片。实现接口List<SlideModel>, IModelChangedObserver。
SlideshowActivity用于播放幻灯片,在Oncreate中SlideshowModel.createFromMessageUri(this, msg)构建SlideshowModel对象:
通过uri获取PduBody对象,通过PduBody获取SMILLayoutElement,接着解析smil文件。利用MediaModelFactory获取MediaMode
public static MediaModel getMediaModel(Context context,
SMILMediaElement sme, LayoutModel layouts, PduBody pb)
并给每一个获取到的MediaModel注册EventListener接口(好像没用,注册者没被引用),最后也把SlideModel给注册了。
SmilHelper.addMediaElementEventListeners(
(EventTarget) sme, media);
获取所有SlideModel后构建SlideshowModel对象,并注册自己的IModelChangedObserver接口,返回SlideshowModel。
接下来
mSlideView = (SlideView) findViewById(R.id.slide_view);
PresenterFactory.getPresenter("SlideshowPresenter", this, mSlideView, model);
在SlideshowPresenter类的构造中调用了父类Presenter的构造并在其中
mModel = model;
mModel.registerModelChangedObserver(this);
其中model为SlideShowModel,Presenter实现接口IModelChangedObserver,也就是说把SlideshowPresenter注册到SlideShowModel。
Model注册方法如下:
public void registerModelChangedObserver(IModelChangedObserver observer) {
if (!mModelChangedObservers.contains(observer)) {
mModelChangedObservers.add(observer);
registerModelChangedObserverInDescendants(observer);
}
}
在SlideShowModel中的registerModelChangedObserverInDescendants方法内给所有SlideModel注册了接口:
protected void registerModelChangedObserverInDescendants(
IModelChangedObserver observer) {
mLayout.registerModelChangedObserver(observer);
for (SlideModel slide : mSlides) {
slide.registerModelChangedObserver(observer);
}
}
而在SlideModel内给所有MediaMode注册了这个observer。
也就是说,SlideShowModel与之内所有要显示的对象都注册了SlideshowPresenter的接口。
调用Model的notifyModelChanged则触发接口的调用,调用到SlideshowPresenter的onModelChanged方法。在onModelChanged方法内根据调用的MediaMode不同对SlideView调用不同的显示。
由前面得知每一个MediaModel的EventListener接口都被注册了,接口方法为handleEvent,而在子MediaModel比如ImageMode中的handleEvent方法中都调用了notifyModelChanged方法。
幻灯片播放流程:
构建播放器SmilPlayer对象。由SlideshowModel获取SMILDocument对象
mSmilDoc = SmilHelper.getDocument(model);
获取过程中对每一个MediaMode注册addMediaElementEventListeners((EventTarget) sme, media);接着初始化播放器
mSmilPlayer.init(mSmilDoc);
public synchronized void init(ElementTime root) {
mRoot = root;
mAllEntries = getTimeline(mRoot, 0, Long.MAX_VALUE);
mMediaTimeUpdatedEvent = ((DocumentEvent) mRoot).createEvent("Event");
mMediaTimeUpdatedEvent.initEvent(MEDIA_TIME_UPDATED_EVENT, false, false);
mActiveElements = new ArrayList<ElementTime>();
}
调用SmilPlayer的play方法启动子线程,在run方法里依次播放每一页幻灯片,切换时调用waitForEntry(long interval)方法,此方法内
((EventTarget) mRoot).dispatchEvent(mMediaTimeUpdatedEvent);
分发所有EventListener接口事件。
二、附件的命名及保存
在MmsProvider中进行命名,在PduPersister中进行附件保存。
以saveDraft为开始流程为例:按流程将调用到SlideShowModel的finalResize方法中,此方法调用
PduPersister.getPduPersister(mContext).updateParts(messageUri, pb, null);
开始附件保存流程,其中第三个参数是InputStream的hashMap。在此方法内调用persistPart方法,persistPart方法内
Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
接着调用persistData方法保存数据,如果此前传来的InputStream的hashMap不为空,以uri为key获取流,否则以uri通过openInputStream获取流。
文件命名在insert数据时provider中的insert方法中进行,尾字符串为cl字段内容,名称:parts_时间_cl字段内容。
三、ContentProvider的openInputStream与openOutputStream
重写ContentProvider的openFile方法实现这两个功能。如下Mms的provider
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// TODO do we even need this anymore?
ParcelFileDescriptor fd;
int match = sURLMatcher.match(uri);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "openFile: uri=" + uri + ", mode=" + mode);
}
switch (match) {
default:
fd = openFileHelper(uri, mode);
}
return fd;
}
openFileHelper方法使用"_data"作为文件路径字段,如果自定义字段存储文件路径,则不使用系统提供的openFileHelper方法或者重写此方法。