Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java

Issue 2758313002: Implement the new Photo picker, part two. (Closed)
Patch Set: Fix scrim color Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.photo_picker;
6
7 import android.app.Activity;
8 import android.content.Context;
9 import android.graphics.Bitmap;
10 import android.graphics.BitmapFactory;
11 import android.graphics.Canvas;
12 import android.graphics.Paint;
13 import android.graphics.PorterDuff;
14 import android.graphics.PorterDuffColorFilter;
15 import android.graphics.Rect;
16 import android.support.v4.content.ContextCompat;
17 import android.support.v7.widget.DefaultItemAnimator;
18 import android.support.v7.widget.GridLayoutManager;
19 import android.support.v7.widget.RecyclerView;
20 import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
21 import android.util.AttributeSet;
22 import android.util.LruCache;
23 import android.view.LayoutInflater;
24 import android.view.MenuItem;
25 import android.view.View;
26 import android.widget.RelativeLayout;
27
28 import org.chromium.chrome.R;
29 import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
30 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
31 import org.chromium.ui.PhotoPickerListener;
32
33 import java.util.List;
34
35 /**
36 * A class for keeping track of common data associated with showing photos in
37 * the photo picker, for example the RecyclerView and the bitmap caches.
38 */
39 public class PickerCategoryView extends RelativeLayout
40 implements FileEnumWorkerTask.FilesEnumeratedCallback, OnMenuItemClickLi stener {
41 // The dialog that owns us.
42 private PhotoPickerDialog mDialog;
43
44 // The view containing the recycler view and the toolbar, etc.
45 private SelectableListLayout<PickerBitmap> mSelectableListLayout;
46
47 // The toolbar at the top of the dialog.
48 private PhotoPickerToolbar mToolbar;
49
50 // Our context.
51 private Context mContext;
52
53 // The list of images on disk, sorted by last-modified first.
54 private List<PickerBitmap> mPickerBitmaps;
55
56 // True if multi-selection is allowed in the picker.
57 private boolean mMultiSelection;
Theresa 2017/03/28 20:40:29 nit: mIsMultiSelectionAllowed? or mIsMultiSelectio
Finnur 2017/03/31 14:26:50 Done.
58
59 // The callback to notify the listener of decisions reached in the picker.
60 private PhotoPickerListener mListener;
61
62 // The recycler view showing the images.
63 private RecyclerView mRecyclerView;
64
65 // The picker adapter for the RecyclerView.
66 private PickerAdapter mPickerAdapter;
67
68 // The selection delegate keeping track of which images are selected.
69 private SelectionDelegate<PickerBitmap> mSelectionDelegate;
70
71 // A low-resolution cache for images. Helpful for cache misses from the high -resolution cache
72 // to avoid showing gray squares (show pixelated versions instead until imag e can be loaded off
73 // disk).
74 private LruCache<String, Bitmap> mLowResBitmaps;
75
76 // A high-resolution cache for images.
77 private LruCache<String, Bitmap> mHighResBitmaps;
78
79 // The number of columns to show. Note: mColumns and mPadding (see below) sh ould both be even
80 // numbers or both odd, not a mix (the column padding will not be of uniform thickness if they
81 // are a mix).
Theresa 2017/03/28 20:40:29 nit: Should these be JavaDoc /** */ comments so th
Finnur 2017/03/31 14:26:50 Done. Unless you mean all of them?
82 private int mColumns;
83
84 // The padding between columns. See also comment for mColumns.
85 private int mPadding;
86
87 // The size of the bitmaps (equal length for width and height).
88 private int mImageSize;
89
90 // The control to show for when an image is selected.
91 private Bitmap mBitmapSelected;
92
93 // The control to show for when an image is not selected.
94 private Bitmap mBitmapUnselected;
95
96 // A worker task for asynchronously enumerating files off the main thread.
97 private FileEnumWorkerTask mWorkerTask;
98
99 public PickerCategoryView(Context context) {
100 super(context);
101 init(context);
102 }
103
104 public PickerCategoryView(Context context, AttributeSet attrs) {
105 super(context, attrs);
106 init(context);
107 }
108
109 public PickerCategoryView(Context context, AttributeSet attrs, int defStyle) {
110 super(context, attrs, defStyle);
111 init(context);
112 }
113
114 /**
115 * A helper function for initializing the PickerCategoryView.
116 * @param context The context to use.
117 */
118 @SuppressWarnings("unchecked") // mSelectableListLayout
119 private void init(Context context) {
120 mContext = context;
121
122 mSelectionDelegate = new SelectionDelegate<PickerBitmap>();
123
124 View root = LayoutInflater.from(context).inflate(R.layout.photo_picker_d ialog, this);
125 mSelectableListLayout =
126 (SelectableListLayout<PickerBitmap>) root.findViewById(R.id.sele ctable_list);
127
128 mPickerAdapter = new PickerAdapter(this);
129 mRecyclerView = mSelectableListLayout.initializeRecyclerView(mPickerAdap ter);
130 mToolbar = (PhotoPickerToolbar) mSelectableListLayout.initializeToolbar(
131 R.layout.photo_picker_toolbar, mSelectionDelegate, R.string.menu _history, null,
132 R.id.file_picker_normal_menu_group, R.id.file_picker_selection_m ode_menu_group,
133 R.color.default_primary_color, false, this);
134
135 View view = ((Activity) context).getWindow().getDecorView();
136 int width = view.getWidth() - view.getPaddingLeft() - view.getPaddingRig ht();
137
138 calculateGridMetrics(width);
139
140 RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(mConte xt, mColumns);
141 mRecyclerView.setHasFixedSize(true);
142 mRecyclerView.setLayoutManager(mLayoutManager);
143 mRecyclerView.setItemAnimator(new DefaultItemAnimator());
Theresa 2017/03/28 20:40:29 This shouldn't be necessary. By default, RecyclerV
Finnur 2017/03/31 14:26:50 Done.
144 mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(mColumns, mPadding));
145
146 mBitmapSelected = BitmapFactory.decodeResource(
147 mContext.getResources(), R.drawable.ic_check_circle_black_24dp);
148 mBitmapUnselected = BitmapFactory.decodeResource(
149 mContext.getResources(), R.drawable.ic_radio_button_unchecked_bl ack_24dp);
150
151 // Apply color to the bitmaps.
152 int prefAccentColor = ContextCompat.getColor(mContext, R.color.pref_acce nt_color);
153 mBitmapSelected = colorBitmap(mBitmapSelected, prefAccentColor);
154 int unselectedColor = ContextCompat.getColor(mContext, R.color.white_mod e_tint);
155 mBitmapUnselected = colorBitmap(mBitmapUnselected, unselectedColor);
Theresa 2017/03/28 20:40:29 Typically we would set the src for an ImageView in
Finnur 2017/03/31 14:26:50 Neat. Done.
156
157 final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
158 final int cacheSizeLarge = maxMemory / 2; // 1/2 of the available memory .
159 final int cacheSizeSmall = maxMemory / 8; // 1/8th of the available memo ry.
160 mLowResBitmaps = new LruCache<String, Bitmap>(cacheSizeSmall) {
161 @Override
162 protected int sizeOf(String key, Bitmap bitmap) {
163 return bitmap.getByteCount() / 1024;
Theresa 2017/03/28 20:40:29 nit: define a constant for 1024
Finnur 2017/03/31 14:26:50 Done.
164 }
165 };
166 mHighResBitmaps = new LruCache<String, Bitmap>(cacheSizeLarge) {
167 @Override
168 protected int sizeOf(String key, Bitmap bitmap) {
169 return bitmap.getByteCount() / 1024;
170 }
171 };
172
173 // TODO(finnur): Remove this once the decoder service is in place.
174 prepareBitmaps();
175 }
176
177 /**
178 * Sets the starting state for the PickerCategoryView object.
179 * @param selectionDelegate The selection delegate to use.
180 * @param listener The listener who should be notified of actions.
181 * @param multiSelection Whether to allow the user to select more than one i mage.
182 */
183 public void setStartingState(
184 PhotoPickerDialog dialog, PhotoPickerListener listener, boolean mult iSelection) {
185 if (!multiSelection) mSelectionDelegate.setSingleSelectionMode();
186
187 mDialog = dialog;
188 mMultiSelection = multiSelection;
189 mListener = listener;
190 }
191
192 // FileEnumWorkerTask.FilesEnumeratedCallback:
193
194 @Override
195 public void filesEnumeratedCallback(List<PickerBitmap> files) {
196 mPickerBitmaps = files;
197 if (files != null && files.size() > 0) {
198 mPickerAdapter.notifyDataSetChanged();
199 } else {
200 setVisibility(View.GONE);
Theresa 2017/03/28 20:40:29 Can this ever be empty, since there's the camera a
Finnur 2017/03/31 14:26:50 Removed. I believe this is from back when the Came
201 }
202 }
203
204 // OnMenuItemClickListener:
205
206 @Override
207 public boolean onMenuItemClick(MenuItem item) {
208 if (item.getItemId() == R.id.close_menu_id
209 || item.getItemId() == R.id.selection_mode_done_menu_id) {
210 tryNotifyPhotoSet();
211 mDialog.dismiss();
212 return true;
213 }
214 return false;
215 }
216
217 // Simple accessors:
218
219 public int getImageSize() {
220 return mImageSize;
221 }
222 public SelectionDelegate<PickerBitmap> getSelectionDelegate() {
223 return mSelectionDelegate;
224 }
225 public List<PickerBitmap> getPickerBitmaps() {
226 return mPickerBitmaps;
227 }
228 public LruCache<String, Bitmap> getLowResBitmaps() {
229 return mLowResBitmaps;
230 }
231 public LruCache<String, Bitmap> getHighResBitmaps() {
232 return mHighResBitmaps;
233 }
234 public boolean isMultiSelect() {
235 return mMultiSelection;
236 }
237
238 /**
239 * Notifies the caller that the user selected to launch the gallery.
240 */
241 public void showGallery() {
242 mListener.onPickerUserAction(PhotoPickerListener.Action.LAUNCH_GALLERY, null);
243 }
244
245 /**
246 * Notifies the caller that the user selected to launch the camera intent.
247 */
248 public void showCamera() {
249 mListener.onPickerUserAction(PhotoPickerListener.Action.LAUNCH_CAMERA, n ull);
250 }
251
252 /**
253 * Returns the selection bitmaps (control indicating whether the image is se lected or not).
254 * @param selected See return value.
255 * @return If |selected| is true, the selection bitmap is returned. Otherwis e the unselection
256 * bitmap is returned.
257 */
258 public Bitmap getSelectionBitmap(boolean selected) {
259 if (selected) {
260 return mBitmapSelected;
261 } else {
262 return mBitmapUnselected;
263 }
264 }
265
266 /**
267 * Applies a color filter to a bitmap.
268 * @param original The bitmap to color.
269 * @param color The color to apply.
270 * @return A colored bitmap.
271 */
272 private Bitmap colorBitmap(Bitmap original, int color) {
273 Bitmap mutable = original.copy(Bitmap.Config.ARGB_8888, true);
274 Canvas canvas = new Canvas(mutable);
275 Paint paint = new Paint();
276 paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SR C_IN));
277 canvas.drawBitmap(mutable, 0.f, 0.f, paint);
278 return mutable;
279 }
280
281 /**
282 * Calculates image size and how many columns can fit on-screen.
283 * @param width The total width of the boundary to show the images in.
284 */
285 private void calculateGridMetrics(int width) {
286 int minSize =
287 mContext.getResources().getDimensionPixelSize(R.dimen.file_picke r_tile_min_size);
288 mPadding = mContext.getResources().getDimensionPixelSize(R.dimen.file_pi cker_tile_gap);
289 mColumns = Math.max(1, (width - mPadding) / (minSize + mPadding));
290 mImageSize = (width - mPadding * (mColumns + 1)) / (mColumns);
291
292 // Make sure columns and padding are either both even or both odd.
293 if (((mColumns % 2) == 0) != ((mPadding % 2) == 0)) {
294 mPadding++;
295 }
296 }
297
298 /**
299 * Prepares bitmaps for loading.
300 */
301 private void prepareBitmaps() {
302 if (mWorkerTask != null) {
303 mWorkerTask.cancel(true);
304 }
305
306 mWorkerTask = new FileEnumWorkerTask(this, new AttrAcceptFileFilter("ima ge/*,video/*"));
307 mWorkerTask.execute();
308 }
309
310 /**
311 * Tries to notify any listeners that one or more photos have been selected.
312 */
313 private void tryNotifyPhotoSet() {
314 List<PickerBitmap> selectedFiles = mSelectionDelegate.getSelectedItems() ;
315 String[] photos = new String[selectedFiles.size()];
316 int i = 0;
317 for (PickerBitmap bitmap : selectedFiles) {
318 photos[i++] = bitmap.getFilePath();
319 }
320
321 mListener.onPickerUserAction(PhotoPickerListener.Action.PHOTOS_SELECTED, photos);
322 }
323
324 /**
325 * A class for implementing grid spacing between items.
326 */
327 private class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
328 // The number of spans to account for.
329 private int mSpanCount;
330
331 // The amount of spacing to use.
332 private int mSpacing;
333
334 public GridSpacingItemDecoration(int spanCount, int spacing) {
335 mSpanCount = spanCount;
336 mSpacing = spacing;
337 }
338
339 @Override
340 public void getItemOffsets(
341 Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
342 int left = 0, right = 0, top = 0, bottom = 0;
343 int position = parent.getChildAdapterPosition(view);
344
345 if (position >= 0) {
346 int column = position % mSpanCount;
347
348 left = mSpacing - column * mSpacing / mSpanCount;
349 right = (column + 1) * mSpacing / mSpanCount;
350
351 if (position < mSpanCount) {
352 top = mSpacing;
353 }
354 bottom = mSpacing;
355 }
356
357 outRect.set(left, top, right, bottom);
358 }
359 }
360 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698