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