VideoItem.java revision 51cafa0a35546a42c573357aa7a031a79cf9ba1b
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.camera.data; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.graphics.Bitmap; 22import android.net.Uri; 23import android.provider.MediaStore; 24import android.view.LayoutInflater; 25import android.view.View; 26import android.widget.ImageView; 27 28import com.android.camera.data.FilmstripItemAttributes.Attributes; 29import com.android.camera.debug.Log; 30import com.android.camera.util.Size; 31import com.android.camera2.R; 32import com.bumptech.glide.Glide; 33import com.google.common.base.Optional; 34 35/** 36 * Backing data for a single video displayed in the filmstrip. 37 */ 38public class VideoItem extends FilmstripItemBase<VideoItemData> { 39 private static class VideoViewHolder { 40 private final ImageView mVideoView; 41 private final ImageView mPlayButton; 42 43 public VideoViewHolder(ImageView videoView, ImageView playButton) { 44 mVideoView = videoView; 45 mPlayButton = playButton; 46 } 47 } 48 49 private static final Log.Tag TAG = new Log.Tag("VideoItem"); 50 51 private static final FilmstripItemAttributes VIDEO_ITEM_ATTRIBUTES = 52 new FilmstripItemAttributes.Builder() 53 .with(Attributes.CAN_SHARE) 54 .with(Attributes.CAN_PLAY) 55 .with(Attributes.CAN_DELETE) 56 .with(Attributes.CAN_SWIPE_AWAY) 57 .with(Attributes.CAN_SWIPE_IN_FULL_SCREEN) 58 .with(Attributes.HAS_DETAILED_CAPTURE_INFO) 59 .with(Attributes.IS_VIDEO) 60 .build(); 61 62 private final VideoItemFactory mVideoItemFactory; 63 64 private Size mCachedSize; 65 66 public VideoItem(Context context, VideoItemData data, VideoItemFactory videoItemFactory) { 67 super(context, data, VIDEO_ITEM_ATTRIBUTES); 68 mVideoItemFactory = videoItemFactory; 69 } 70 71 /** 72 * We can't trust the media store and we can't afford the performance overhead of 73 * synchronously decoding the video header for every item when loading our data set 74 * from the media store, so we instead run the metadata loader in the background 75 * to decode the video header for each item and prefer whatever values it obtains. 76 */ 77 private int getBestWidth() { 78 int metadataWidth = mMetaData.getVideoWidth(); 79 if (metadataWidth > 0) { 80 return metadataWidth; 81 } else { 82 return mData.getDimensions().getWidth(); 83 } 84 } 85 86 private int getBestHeight() { 87 int metadataHeight = mMetaData.getVideoHeight(); 88 if (metadataHeight > 0) { 89 return metadataHeight; 90 } else { 91 return mData.getDimensions().getHeight(); 92 } 93 } 94 95 /** 96 * If the metadata loader has determined from the video header that we need to rotate the video 97 * 90 or 270 degrees, then we swap the width and height. 98 */ 99 public int getWidth() { 100 return mMetaData.isVideoRotated() ? getBestHeight() : getBestWidth(); 101 } 102 103 public int getHeight() { 104 return mMetaData.isVideoRotated() ? getBestWidth() : getBestHeight(); 105 } 106 107 @Override 108 public Size getDimensions() { 109 int width = getWidth(); 110 int height = getHeight(); 111 if (mCachedSize == null || 112 width != mCachedSize.getWidth() || height != mCachedSize.getHeight()) { 113 mCachedSize = new Size(width, height); 114 } 115 return mCachedSize; 116 } 117 118 @Override 119 public boolean delete() { 120 ContentResolver cr = mContext.getContentResolver(); 121 cr.delete(VideoDataQuery.CONTENT_URI, 122 MediaStore.Video.VideoColumns._ID + "=" + mData.getContentId(), null); 123 return super.delete(); 124 } 125 126 @Override 127 public Optional<MediaDetails> getMediaDetails() { 128 Optional<MediaDetails> optionalDetails = super.getMediaDetails(); 129 if (optionalDetails.isPresent()) { 130 MediaDetails mediaDetails = optionalDetails.get(); 131 String duration = MediaDetails.formatDuration(mContext, mData.getVideoDurationMillis()); 132 mediaDetails.addDetail(MediaDetails.INDEX_DURATION, duration); 133 } 134 return optionalDetails; 135 } 136 137 @Override 138 public FilmstripItem refresh() { 139 return mVideoItemFactory.get(mData.getUri()); 140 } 141 142 @Override 143 public View getView(Optional<View> optionalView, int viewWidthPx, int viewHeightPx, 144 LocalFilmstripDataAdapter adapter, boolean isInProgress, 145 VideoClickedCallback videoClickedCallback) { 146 View view; 147 VideoViewHolder viewHolder; 148 149 if (optionalView.isPresent()) { 150 view = optionalView.get(); 151 viewHolder = (VideoViewHolder) view.getTag(R.id.mediadata_tag_target); 152 } else { 153 view = LayoutInflater.from(mContext).inflate(R.layout.filmstrip_video, null); 154 view.setTag(R.id.mediadata_tag_viewtype, getItemViewType().ordinal()); 155 ImageView videoView = (ImageView) view.findViewById(R.id.video_view); 156 ImageView playButton = (ImageView) view.findViewById(R.id.play_button); 157 158 viewHolder = new VideoViewHolder(videoView, playButton); 159 view.setTag(R.id.mediadata_tag_target, viewHolder); 160 } 161 162 fillVideoView(view, viewHolder, viewWidthPx, viewHeightPx, videoClickedCallback); 163 164 return view; 165 } 166 167 private void fillVideoView(View view, VideoViewHolder viewHolder, final int viewWidthPx, 168 final int viewHeightPx, final VideoClickedCallback videoClickedCallback) { 169 170 //TODO: Figure out why these can be <= 0. 171 if (viewWidthPx <= 0 || viewHeightPx <=0) { 172 return; 173 } 174 175 Uri uri = mData.getUri(); 176 177 glideFilmstripThumb(uri, viewWidthPx, viewHeightPx) 178 .thumbnail(glideMediaStoreThumb(uri)) 179 .placeholder(DEFAULT_PLACEHOLDER_RESOURCE) 180 .dontAnimate() 181 .into(viewHolder.mVideoView); 182 183 // ImageView for the play icon. 184 viewHolder.mPlayButton.setOnClickListener(new View.OnClickListener() { 185 @Override 186 public void onClick(View v) { 187 videoClickedCallback.playVideo(mData.getUri(), mData.getTitle()); 188 } 189 }); 190 191 view.setContentDescription(mContext.getResources().getString( 192 R.string.video_date_content_description, 193 mDateFormatter.format(mData.getLastModifiedDate()))); 194 } 195 196 197 @Override 198 public void recycle(View view) { 199 VideoViewHolder videoViewHolder = 200 (VideoViewHolder) view.getTag(R.id.mediadata_tag_target); 201 Glide.clear(videoViewHolder.mVideoView); 202 } 203 204 @Override 205 public FilmstripItemType getItemViewType() { 206 return FilmstripItemType.VIDEO; 207 } 208 209 @Override 210 public Optional<Bitmap> generateThumbnail(int boundingWidthPx, int boundingHeightPx) { 211 return Optional.of(FilmstripItemUtils.loadVideoThumbnail(getData().getFilePath())); 212 } 213 214 @Override 215 public String toString() { 216 return "VideoItem: " + mData.toString(); 217 } 218} 219