1/* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.mms.model; 19 20import java.io.IOException; 21 22import org.w3c.dom.smil.SMILMediaElement; 23import org.w3c.dom.smil.SMILRegionElement; 24import org.w3c.dom.smil.SMILRegionMediaElement; 25import org.w3c.dom.smil.Time; 26import org.w3c.dom.smil.TimeList; 27 28import android.content.Context; 29import android.util.Log; 30 31import com.android.mms.LogTag; 32import com.android.mms.MmsConfig; 33import com.google.android.mms.ContentType; 34import com.google.android.mms.MmsException; 35import com.google.android.mms.pdu.PduBody; 36import com.google.android.mms.pdu.PduPart; 37 38public class MediaModelFactory { 39 private static final String TAG = "Mms:media"; 40 41 public static MediaModel getMediaModel(Context context, 42 SMILMediaElement sme, LayoutModel layouts, PduBody pb) 43 throws IOException, IllegalArgumentException, MmsException { 44 String tag = sme.getTagName(); 45 String src = sme.getSrc(); 46 PduPart part = findPart(pb, src); 47 48 if (sme instanceof SMILRegionMediaElement) { 49 return getRegionMediaModel( 50 context, tag, src, (SMILRegionMediaElement) sme, layouts, part); 51 } else { 52 return getGenericMediaModel( 53 context, tag, src, sme, part, null); 54 } 55 } 56 57 private static PduPart findPart(PduBody pb, String src) { 58 PduPart part = null; 59 60 if (src != null) { 61 src = unescapeXML(src); 62 if (src.startsWith("cid:")) { 63 part = pb.getPartByContentId("<" + src.substring("cid:".length()) + ">"); 64 } else { 65 part = pb.getPartByName(src); 66 if (part == null) { 67 part = pb.getPartByFileName(src); 68 if (part == null) { 69 part = pb.getPartByContentLocation(src); 70 } 71 } 72 } 73 } 74 75 if (part != null) { 76 return part; 77 } 78 79 throw new IllegalArgumentException("No part found for the model."); 80 } 81 82 private static String unescapeXML(String str) { 83 return str.replaceAll("<","<") 84 .replaceAll(">", ">") 85 .replaceAll(""","\"") 86 .replaceAll("'","'") 87 .replaceAll("&", "&"); 88 } 89 90 private static MediaModel getRegionMediaModel(Context context, 91 String tag, String src, SMILRegionMediaElement srme, 92 LayoutModel layouts, PduPart part) throws IOException, MmsException { 93 SMILRegionElement sre = srme.getRegion(); 94 if (sre != null) { 95 RegionModel region = layouts.findRegionById(sre.getId()); 96 if (region != null) { 97 return getGenericMediaModel(context, tag, src, srme, part, region); 98 } 99 } else { 100 String rId = null; 101 102 if (tag.equals(SmilHelper.ELEMENT_TAG_TEXT)) { 103 rId = LayoutModel.TEXT_REGION_ID; 104 } else { 105 rId = LayoutModel.IMAGE_REGION_ID; 106 } 107 108 RegionModel region = layouts.findRegionById(rId); 109 if (region != null) { 110 return getGenericMediaModel(context, tag, src, srme, part, region); 111 } 112 } 113 114 throw new IllegalArgumentException("Region not found or bad region ID."); 115 } 116 117 // When we encounter a content type we can't handle, such as "application/vnd.smaf", instead 118 // of throwing an exception and crashing, insert an empty TextModel in its place. 119 private static MediaModel createEmptyTextModel(Context context, RegionModel regionModel) 120 throws IOException { 121 return new TextModel(context, ContentType.TEXT_PLAIN, null, regionModel); 122 } 123 124 private static MediaModel getGenericMediaModel(Context context, 125 String tag, String src, SMILMediaElement sme, PduPart part, 126 RegionModel regionModel) throws IOException, MmsException { 127 byte[] bytes = part.getContentType(); 128 if (bytes == null) { 129 throw new IllegalArgumentException( 130 "Content-Type of the part may not be null."); 131 } 132 133 String contentType = new String(bytes); 134 MediaModel media = null; 135 if (tag.equals(SmilHelper.ELEMENT_TAG_TEXT)) { 136 media = new TextModel(context, contentType, src, 137 part.getCharset(), part.getData(), regionModel); 138 } else if (tag.equals(SmilHelper.ELEMENT_TAG_IMAGE)) { 139 media = new ImageModel(context, contentType, src, 140 part.getDataUri(), regionModel); 141 } else if (tag.equals(SmilHelper.ELEMENT_TAG_VIDEO)) { 142 media = new VideoModel(context, contentType, src, 143 part.getDataUri(), regionModel); 144 } else if (tag.equals(SmilHelper.ELEMENT_TAG_AUDIO)) { 145 media = new AudioModel(context, contentType, src, 146 part.getDataUri()); 147 } else if (tag.equals(SmilHelper.ELEMENT_TAG_REF)) { 148 if (ContentType.isTextType(contentType)) { 149 media = new TextModel(context, contentType, src, 150 part.getCharset(), part.getData(), regionModel); 151 } else if (ContentType.isImageType(contentType)) { 152 media = new ImageModel(context, contentType, src, 153 part.getDataUri(), regionModel); 154 } else if (ContentType.isVideoType(contentType)) { 155 media = new VideoModel(context, contentType, src, 156 part.getDataUri(), regionModel); 157 } else if (ContentType.isAudioType(contentType)) { 158 media = new AudioModel(context, contentType, src, 159 part.getDataUri()); 160 } else { 161 Log.d(TAG, "[MediaModelFactory] getGenericMediaModel Unsupported Content-Type: " 162 + contentType); 163 media = createEmptyTextModel(context, regionModel); 164 } 165 } else { 166 throw new IllegalArgumentException("Unsupported TAG: " + tag); 167 } 168 169 // Set 'begin' property. 170 int begin = 0; 171 TimeList tl = sme.getBegin(); 172 if ((tl != null) && (tl.getLength() > 0)) { 173 // We only support a single begin value. 174 Time t = tl.item(0); 175 begin = (int) (t.getResolvedOffset() * 1000); 176 } 177 media.setBegin(begin); 178 179 // Set 'duration' property. 180 int duration = (int) (sme.getDur() * 1000); 181 if (duration <= 0) { 182 tl = sme.getEnd(); 183 if ((tl != null) && (tl.getLength() > 0)) { 184 // We only support a single end value. 185 Time t = tl.item(0); 186 if (t.getTimeType() != Time.SMIL_TIME_INDEFINITE) { 187 duration = (int) (t.getResolvedOffset() * 1000) - begin; 188 189 if (duration == 0 && 190 (media instanceof AudioModel || media instanceof VideoModel)) { 191 duration = MmsConfig.getMinimumSlideElementDuration(); 192 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 193 Log.d(TAG, "[MediaModelFactory] compute new duration for " + tag + 194 ", duration=" + duration); 195 } 196 } 197 } 198 } 199 } 200 201 media.setDuration(duration); 202 203 if (!MmsConfig.getSlideDurationEnabled()) { 204 /** 205 * Because The slide duration is not supported by mmsc, 206 * the device has to set fill type as FILL_FREEZE. 207 * If not, the media will disappear while rotating the screen 208 * in the slide show play view. 209 */ 210 media.setFill(sme.FILL_FREEZE); 211 } else { 212 // Set 'fill' property. 213 media.setFill(sme.getFill()); 214 } 215 return media; 216 } 217} 218