VideoItem.java revision b6a8810b7a5ac0df1e19efd26628c01bcb32b97b
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.provider.MediaStore; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.widget.ImageView; 26 27import com.android.camera.data.FilmstripItemAttributes.Attributes; 28import com.android.camera.debug.Log; 29import com.android.camera.util.Size; 30import com.android.camera2.R; 31import com.bumptech.glide.Glide; 32import com.google.common.base.Optional; 33 34import javax.annotation.Nonnull; 35 36/** 37 * Backing data for a single video displayed in the filmstrip. 38 */ 39public class VideoItem extends FilmstripItemBase<VideoItemData> { 40 private static class VideoViewHolder { 41 private final ImageView mVideoView; 42 private final ImageView mPlayButton; 43 44 public VideoViewHolder(ImageView videoView, ImageView playButton) { 45 mVideoView = videoView; 46 mPlayButton = playButton; 47 } 48 } 49 50 private static final Log.Tag TAG = new Log.Tag("VideoItem"); 51 52 private static final FilmstripItemAttributes VIDEO_ITEM_ATTRIBUTES = 53 new FilmstripItemAttributes.Builder() 54 .with(Attributes.CAN_SHARE) 55 .with(Attributes.CAN_PLAY) 56 .with(Attributes.CAN_DELETE) 57 .with(Attributes.CAN_SWIPE_AWAY) 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, GlideFilmstripManager manager, VideoItemData data, 67 VideoItemFactory videoItemFactory) { 68 super(context, manager, data, VIDEO_ITEM_ATTRIBUTES); 69 mVideoItemFactory = videoItemFactory; 70 } 71 72 /** 73 * We can't trust the media store and we can't afford the performance overhead of 74 * synchronously decoding the video header for every item when loading our data set 75 * from the media store, so we instead run the metadata loader in the background 76 * to decode the video header for each item and prefer whatever values it obtains. 77 */ 78 private int getBestWidth() { 79 int metadataWidth = mMetaData.getVideoWidth(); 80 if (metadataWidth > 0) { 81 return metadataWidth; 82 } else { 83 return mData.getDimensions().getWidth(); 84 } 85 } 86 87 private int getBestHeight() { 88 int metadataHeight = mMetaData.getVideoHeight(); 89 if (metadataHeight > 0) { 90 return metadataHeight; 91 } else { 92 return mData.getDimensions().getHeight(); 93 } 94 } 95 96 /** 97 * If the metadata loader has determined from the video header that we need to rotate the video 98 * 90 or 270 degrees, then we swap the width and height. 99 */ 100 public int getWidth() { 101 return mMetaData.isVideoRotated() ? getBestHeight() : getBestWidth(); 102 } 103 104 public int getHeight() { 105 return mMetaData.isVideoRotated() ? getBestWidth() : getBestHeight(); 106 } 107 108 @Override 109 public Size getDimensions() { 110 int width = getWidth(); 111 int height = getHeight(); 112 if (mCachedSize == null || 113 width != mCachedSize.getWidth() || height != mCachedSize.getHeight()) { 114 mCachedSize = new Size(width, height); 115 } 116 return mCachedSize; 117 } 118 119 @Override 120 public boolean delete() { 121 ContentResolver cr = mContext.getContentResolver(); 122 cr.delete(VideoDataQuery.CONTENT_URI, 123 MediaStore.Video.VideoColumns._ID + "=" + mData.getContentId(), null); 124 return super.delete(); 125 } 126 127 @Override 128 public Optional<MediaDetails> getMediaDetails() { 129 Optional<MediaDetails> optionalDetails = super.getMediaDetails(); 130 if (optionalDetails.isPresent()) { 131 MediaDetails mediaDetails = optionalDetails.get(); 132 String duration = MediaDetails.formatDuration(mContext, mData.getVideoDurationMillis()); 133 mediaDetails.addDetail(MediaDetails.INDEX_DURATION, duration); 134 } 135 return optionalDetails; 136 } 137 138 @Override 139 public FilmstripItem refresh() { 140 return mVideoItemFactory.get(mData.getUri()); 141 } 142 143 @Override 144 public View getView(Optional<View> optionalView, 145 LocalFilmstripDataAdapter adapter, boolean isInProgress, 146 final VideoClickedCallback videoClickedCallback) { 147 148 View view; 149 VideoViewHolder viewHolder; 150 151 if (optionalView.isPresent()) { 152 view = optionalView.get(); 153 viewHolder = getViewHolder(view); 154 } else { 155 view = LayoutInflater.from(mContext).inflate(R.layout.filmstrip_video, null); 156 view.setTag(R.id.mediadata_tag_viewtype, getItemViewType().ordinal()); 157 ImageView videoView = (ImageView) view.findViewById(R.id.video_view); 158 ImageView playButton = (ImageView) view.findViewById(R.id.play_button); 159 160 viewHolder = new VideoViewHolder(videoView, playButton); 161 view.setTag(R.id.mediadata_tag_target, viewHolder); 162 } 163 164 if (viewHolder != null) { 165 // ImageView for the play icon. 166 viewHolder.mPlayButton.setOnClickListener(new View.OnClickListener() { 167 @Override 168 public void onClick(View v) { 169 videoClickedCallback.playVideo(mData.getUri(), mData.getTitle()); 170 } 171 }); 172 173 view.setContentDescription(mContext.getResources().getString( 174 R.string.video_date_content_description, 175 mDateFormatter.format(mData.getLastModifiedDate()))); 176 177 renderTiny(viewHolder); 178 } else { 179 Log.w(TAG, "getView called with a view that is not compatible with VideoItem."); 180 } 181 182 return view; 183 } 184 185 @Override 186 public void renderTiny(@Nonnull View view) { 187 renderTiny(getViewHolder(view)); 188 } 189 190 @Override 191 public void renderThumbnail(@Nonnull View view) { 192 mGlideManager.loadScreen(mData.getUri(), generateSignature(mData), 193 mSuggestedWidthPx, mSuggestedHeightPx) 194 .thumbnail(mGlideManager.loadMediaStoreThumb(mData.getUri(), 195 generateSignature(mData))) 196 .into(getViewHolder(view).mVideoView); 197 } 198 199 @Override 200 public void renderFullRes(@Nonnull View view) { } 201 202 @Override 203 public void recycle(@Nonnull View view) { 204 VideoViewHolder holder = getViewHolder(view); 205 if (holder != null) { 206 Glide.clear(getViewHolder(view).mVideoView); 207 } 208 } 209 210 @Override 211 public FilmstripItemType getItemViewType() { 212 return FilmstripItemType.VIDEO; 213 } 214 215 @Override 216 public Optional<Bitmap> generateThumbnail(int boundingWidthPx, int boundingHeightPx) { 217 return Optional.of(FilmstripItemUtils.loadVideoThumbnail(getData().getFilePath())); 218 } 219 220 @Override 221 public String toString() { 222 return "VideoItem: " + mData.toString(); 223 } 224 225 private void renderTiny(@Nonnull VideoViewHolder viewHolder) { 226 mGlideManager.loadMediaStoreThumb(mData.getUri(), generateSignature(mData)) 227 .into(viewHolder.mVideoView); 228 } 229 230 private VideoViewHolder getViewHolder(@Nonnull View view) { 231 Object container = view.getTag(R.id.mediadata_tag_target); 232 if (container instanceof VideoViewHolder) { 233 return (VideoViewHolder) container; 234 } 235 236 return null; 237 } 238} 239