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