Home | History | Annotate | Download | only in data
      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 
     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 
     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      */
     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 
     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      */
    102     public int getWidth() {
    103         return mMetaData.isVideoRotated() ? getBestHeight() : getBestWidth();
    104     }
    105 
    106     public int getHeight() {
    107         return mMetaData.isVideoRotated() ?  getBestWidth() : getBestHeight();
    108     }
    109 
    110     @Override
    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
    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
    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
    142     public FilmstripItem refresh() {
    143         return mVideoItemFactory.get(mData.getUri());
    144     }
    145 
    146     @Override
    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
    189     public void renderTiny(@Nonnull View view) {
    190         renderTiny(getViewHolder(view));
    191     }
    192 
    193     @Override
    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
    202     public void renderFullRes(@Nonnull View view) { }
    203 
    204     @Override
    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
    213     public FilmstripItemType getItemViewType() {
    214         return FilmstripItemType.VIDEO;
    215     }
    216 
    217     @Override
    218     public Optional<Bitmap> generateThumbnail(int boundingWidthPx, int boundingHeightPx) {
    219         return Optional.fromNullable(FilmstripItemUtils.loadVideoThumbnail(
    220                 getData().getFilePath()));
    221     }
    222 
    223     @Override
    224     public String toString() {
    225         return "VideoItem: " + mData.toString();
    226     }
    227 
    228     private void renderTiny(@Nonnull VideoViewHolder viewHolder) {
    229         mGlideManager.loadMediaStoreThumb(mData.getUri(), generateSignature(mData))
    230               .into(viewHolder.mVideoView);
    231     }
    232 
    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