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