ProjectPickerAdapter.java revision 7347ed1e162009eeeb3113eafaebbc39a9f67fda
1/* 2 * Copyright (C) 2011 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.videoeditor; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.graphics.Canvas; 24import android.graphics.Color; 25import android.graphics.Paint; 26import android.graphics.Paint.Style; 27import android.graphics.Typeface; 28import android.media.videoeditor.VideoEditor; 29import android.os.AsyncTask; 30import android.text.format.DateUtils; 31import android.util.LruCache; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import android.widget.BaseAdapter; 36import android.widget.ImageView; 37import android.widget.TextView; 38 39import com.android.videoeditor.service.VideoEditorProject; 40import com.android.videoeditor.util.ImageUtils; 41 42import java.io.File; 43import java.io.IOException; 44import java.util.List; 45 46 47public class ProjectPickerAdapter extends BaseAdapter { 48 private Context mContext; 49 private Resources mResources; 50 private LayoutInflater mInflater; 51 private List<VideoEditorProject> mProjects; 52 private int mItemWidth; 53 private int mItemHeight; 54 private LruCache<String, Bitmap> mPreviewBitmapCache; 55 56 public ProjectPickerAdapter(Context context, LayoutInflater inflater, 57 List<VideoEditorProject> projects) { 58 mContext = context; 59 mResources = context.getResources(); 60 mInflater = inflater; 61 mProjects = projects; 62 mItemWidth = (int) mResources.getDimension(R.dimen.project_picker_item_width); 63 mItemHeight = (int) mResources.getDimension(R.dimen.project_picker_item_height); 64 65 // Limit the cache size to 15 thumbnails. 66 mPreviewBitmapCache = new LruCache<String, Bitmap>(15); 67 } 68 69 /** 70 * Clears project list and update display. 71 */ 72 public void clear() { 73 mProjects.clear(); 74 notifyDataSetChanged(); 75 } 76 77 /** 78 * Removes the project with specified {@code projectPath} from the project list and updates the 79 * display. 80 * 81 * @param projectPath The project path of the to-be-removed project 82 * @return {code true} if the project is successfully removed, 83 * {code false} if no removal happened 84 */ 85 public boolean remove(String projectPath) { 86 for (VideoEditorProject project : mProjects) { 87 if (project.getPath().equals(projectPath)) { 88 if (mProjects.remove(project)) { 89 notifyDataSetChanged(); 90 return true; 91 } else { 92 return false; 93 } 94 } 95 } 96 return false; 97 } 98 99 @Override 100 public int getCount() { 101 // Add one to represent an additional dummy project for "create new project" option. 102 return mProjects.size() + 1; 103 } 104 105 @Override 106 public Object getItem(int position) { 107 if (position == mProjects.size()) { 108 return null; 109 } 110 return mProjects.get(position); 111 } 112 113 @Override 114 public long getItemId(int position) { 115 return position; 116 } 117 118 @Override 119 public View getView(int position, View convertView, ViewGroup parent) { 120 // Inflate a new view with project thumbnail and information. 121 // We never reuse convertView because we load thumbnails asynchronously 122 // and hook an async task with the new view. If the new view is reused 123 // as a convertView, the async task might put a wrong thumbnail on it. 124 View v = mInflater.inflate(R.layout.project_picker_item, null); 125 ImageView iv = (ImageView) v.findViewById(R.id.thumbnail); 126 Bitmap thumbnail; 127 TextView titleView = (TextView) v.findViewById(R.id.title); 128 TextView durationView = (TextView) v.findViewById(R.id.duration); 129 if (position == mProjects.size()) { 130 thumbnail = renderNewProjectThumbnail(); 131 titleView.setTypeface(Typeface.DEFAULT, Typeface.ITALIC); 132 titleView.setText(mContext.getString(R.string.projects_new_project)); 133 durationView.setText(""); 134 } else { 135 VideoEditorProject project = mProjects.get(position); 136 thumbnail = getThumbnail(project.getPath(), iv); 137 titleView.setText(project.getName()); 138 durationView.setText(millisecondsToTimeString(project.getProjectDuration())); 139 } 140 141 if (thumbnail != null) { 142 iv.setImageBitmap(thumbnail); 143 } 144 145 return v; 146 } 147 148 private Bitmap getThumbnail(String projectPath, ImageView imageView) { 149 Bitmap previewBitmap = mPreviewBitmapCache.get(projectPath); 150 if (previewBitmap == null) { 151 // Cache miss: asynchronously load bitmap to avoid scroll stuttering 152 // in the project picker. 153 new LoadPreviewBitmapTask(projectPath, imageView, mItemWidth, mItemHeight, 154 mPreviewBitmapCache).execute(); 155 } else { 156 return previewBitmap; 157 } 158 159 return null; 160 } 161 162 private Bitmap renderNewProjectThumbnail() { 163 final Bitmap bitmap = Bitmap.createBitmap(mItemWidth, mItemHeight, 164 Bitmap.Config.ARGB_8888); 165 final Canvas canvas = new Canvas(bitmap); 166 final Paint paint = new Paint(); 167 168 paint.setColor(Color.WHITE); 169 paint.setStyle(Style.STROKE); 170 // Hairline mode, 1 pixel wide. 171 paint.setStrokeWidth(0); 172 173 canvas.drawRect(0, 0, mItemWidth-1, mItemHeight-1, paint); 174 175 paint.setTextSize(18.0f); 176 paint.setAlpha(255); 177 final Bitmap newProjectIcon = BitmapFactory.decodeResource(mResources, 178 R.drawable.ic_menu_add_video); 179 final int x = (mItemWidth - newProjectIcon.getWidth()) / 2; 180 final int y = (mItemHeight - newProjectIcon.getHeight()) / 2; 181 canvas.drawBitmap(newProjectIcon, x, y, paint); 182 newProjectIcon.recycle(); 183 184 return bitmap; 185 } 186 187 /** 188 * Converts milliseconds into the string time format HH:mm:ss. 189 */ 190 private String millisecondsToTimeString(long milliseconds) { 191 return DateUtils.formatElapsedTime(milliseconds / 1000); 192 } 193} 194 195/** 196 * Worker that loads preview bitmap for a project, 197 */ 198class LoadPreviewBitmapTask extends AsyncTask<Void, Void, Bitmap> { 199 private String mProjectPath; 200 // Handle to the image view we should update when the preview bitmap is loaded. 201 private ImageView mImageView; 202 private int mWidth; 203 private int mHeight; 204 private LruCache<String, Bitmap> mPreviewBitmapCache; 205 206 public LoadPreviewBitmapTask(String projectPath, ImageView imageView, 207 int width, int height, LruCache<String, Bitmap> previewBitmapCache) { 208 mProjectPath = projectPath; 209 mImageView = imageView; 210 mWidth = width; 211 mHeight = height; 212 mPreviewBitmapCache = previewBitmapCache; 213 } 214 215 @Override 216 protected Bitmap doInBackground(Void... param) { 217 final File thumbnail = new File(mProjectPath, VideoEditor.THUMBNAIL_FILENAME); 218 // Return early if thumbnail does not exist. 219 if (!thumbnail.exists()) { 220 return null; 221 } 222 223 try { 224 final Bitmap previewBitmap = ImageUtils.scaleImage( 225 thumbnail.getAbsolutePath(), 226 mWidth - 10, 227 mHeight - 10, 228 ImageUtils.MATCH_SMALLER_DIMENSION); 229 if (previewBitmap != null) { 230 final Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, 231 Bitmap.Config.ARGB_8888); 232 final Canvas canvas = new Canvas(bitmap); 233 final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 234 235 // Draw bitmap at the center of the canvas. 236 canvas.drawBitmap(previewBitmap, 237 (mWidth - previewBitmap.getWidth()) / 2, 238 (mHeight - previewBitmap.getHeight()) / 2, 239 paint); 240 return bitmap; 241 } 242 } catch (IOException e) { 243 e.printStackTrace(); 244 } 245 return null; 246 } 247 248 @Override 249 protected void onPostExecute(Bitmap result) { 250 // If we successfully load the preview bitmap, update the image view. 251 if (result != null) { 252 mPreviewBitmapCache.put(mProjectPath, result); 253 mImageView.setImageBitmap(result); 254 } 255 } 256} 257