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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/ntp/InterestsItemView.java

Issue 1459593002: Added a UI for the Interests Prototype. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years 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 2015 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.ntp;
6
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.content.res.TypedArray;
10 import android.graphics.Bitmap;
11 import android.graphics.BitmapFactory;
12 import android.graphics.Color;
13 import android.graphics.Rect;
14 import android.graphics.drawable.BitmapDrawable;
15 import android.graphics.drawable.Drawable;
16 import android.media.ThumbnailUtils;
17 import android.os.AsyncTask;
18 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
19 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
20 import android.support.v7.widget.AppCompatTextView;
21 import android.text.TextUtils;
22 import android.util.LruCache;
23 import android.util.TypedValue;
24 import android.view.Gravity;
25 import android.view.View;
26 import android.view.View.OnClickListener;
27
28 import org.chromium.base.Log;
29 import org.chromium.chrome.R;
30 import org.chromium.chrome.browser.ntp.InterestsPage.InterestsClickListener;
31 import org.chromium.chrome.browser.ntp.InterestsService.Interest;
32 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
33
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.net.URL;
37 import java.util.LinkedList;
38 import java.util.List;
39
40 /**
41 * Displays the interest name along with an image of it. This item can be clicke d.
42 */
43 class InterestsItemView extends AppCompatTextView implements OnClickListener {
44
45 private static final String TAG = "InterestsItemView";
46
47 /**
48 * Drawing-related values that can be shared between instances of InterestsI temView.
49 */
50 static final class DrawingData {
newt (away) 2015/12/12 00:30:30 make this private
PEConn 2015/12/14 17:05:15 The DrawingData instance is created and held by th
51
52 private final int mPadding;
53 private final int mMinHeight;
54 private final int mImageSize;
55 private final int mTextSize;
56 private final int mImageTextSize;
57
58 /**
59 * Initialize shared values used for drawing the image.
60 *
61 * @param context The view context in which the InterestsItemView will b e drawn.
62 */
63 DrawingData(Context context) {
64 Resources res = context.getResources();
65 mPadding = res.getDimensionPixelOffset(R.dimen.ntp_list_item_padding );
66 mMinHeight = res.getDimensionPixelSize(R.dimen.ntp_interest_item_min _height);
67 mTextSize = res.getDimensionPixelSize(R.dimen.ntp_interest_item_text _size);
68 mImageSize = res.getDimensionPixelSize(R.dimen.ntp_interest_item_ima ge_size);
69 mImageTextSize = res.getDimensionPixelSize(R.dimen.ntp_interest_item _image_text_size);
70 }
71 }
72
73 private Interest mInterest;
74
75 private final Context mContext;
76 private final DrawingData mDrawingData;
77 private final LruCache<String, ImagePlaceholder> mImageCache;
78 private final InterestsClickListener mListener;
79 private final RoundedIconGenerator mIconGenerator;
80
81 /**
82 * @param context The view context in which this item will be shown.
83 * @param interest The interest to display.
84 * @param listener Callback object for when a view is pressed.
85 * @param imageCache A cache to store downloaded images.
86 * @param drawingData Information about the view size.
87 */
88 InterestsItemView(Context context, Interest interest, InterestsClickListener listener,
89 LruCache<String, ImagePlaceholder> imageCache, DrawingData drawingDa ta) {
90 super(context);
91
92 mContext = context;
93 mListener = listener;
94 mImageCache = imageCache;
95 mDrawingData = drawingData;
96
97 setTextSize(TypedValue.COMPLEX_UNIT_PX, mDrawingData.mTextSize);
98 setMinimumHeight(mDrawingData.mMinHeight);
99 setGravity(Gravity.CENTER);
100 setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
101
102 mIconGenerator = new RoundedIconGenerator(
newt (away) 2015/12/12 00:30:30 It'd be worth reusing the RoundedIconGenerator (e.
PEConn 2015/12/14 17:05:15 Done.
103 mDrawingData.mImageSize,
104 mDrawingData.mImageSize,
105 mDrawingData.mImageSize / 2,
106 Color.GRAY,
107 mDrawingData.mImageTextSize);
108
109 setOnClickListener(this);
110
111 reset(interest);
112 }
113
114 /**
115 * Resets the view contents so that it can be reused in the grid view.
116 *
117 * @param interest The interest to display.
118 */
119 public void reset(Interest interest) {
120 // Reset Drawable state so ripples don't continue when the View is reuse d.
121 jumpDrawablesToCurrentState();
122
123 // Exit early if this View is already displaying the Interest given.
124 if (mInterest != null
125 && TextUtils.equals(interest.getName(), mInterest.getName())
newt (away) 2015/12/12 00:30:31 Seems like you should add an equals() method inter
PEConn 2015/12/14 17:05:15 An interest contains a name, an image url and a re
newt (away) 2015/12/14 21:18:39 Gotcha. Thanks for explaining.
126 && TextUtils.equals(interest.getImageUrl(), mInterest.getImageUr l())) {
127 mInterest = interest;
128 return;
129 }
130
131 mInterest = interest;
132
133 setText(mInterest.getName());
134
135 ImagePlaceholder placeholder = mImageCache.get(mInterest.getImageUrl());
136 if (placeholder == null) {
137 // Create a new placeholder, add it to the cache and set it download ing.
138 placeholder = new ImagePlaceholder();
139 mImageCache.put(mInterest.getImageUrl(), placeholder);
140 new ImageDownloadTask(mInterest.getImageUrl(), placeholder, getResou rces()).execute();
141 }
142
143 if (placeholder.isFilled()) {
144 setImage(placeholder.get());
145 } else {
146 // Add a callback to a subclass that will call setImage once the pla ceholder is filled.
147 placeholder.addListener(new ImageDownloadedCallback());
148
149 // Display a letter tile in the meantime.
newt (away) 2015/12/12 00:30:31 It might look a bit jumpy to generate default icon
PEConn 2015/12/14 17:05:15 Acknowledged.
150 mIconGenerator.setBackgroundColor(getTileColor(mInterest.getName())) ;
151 setImage(new BitmapDrawable(mContext.getResources(),
152 mIconGenerator.generateIconForText(mInterest.getName())));
153 }
154 }
155
156 /**
157 * @return The image URL for the interest.
158 */
159 public String getImageUrl() {
160 return mInterest.getImageUrl();
161 }
162
163 /**
164 * @return The name of the interest.
165 */
166 public String getName() {
167 return mInterest.getName();
168 }
169
170 private void setImage(Drawable image) {
171 image.setBounds(new Rect(0, 0, mDrawingData.mImageSize, mDrawingData.mIm ageSize));
172 setCompoundDrawables(null, image, null, null);
173 }
174
175 private int getTileColor(String str) {
176 // Rough copy of LetterTileDrawable.pickColor.
177 // TODO(peconn): Move this to a more general class.
178 TypedArray colors = mContext.getResources().obtainTypedArray(R.array.let ter_tile_colors);
179 return colors.getColor(Math.abs(str.hashCode() % colors.length()), Color .DKGRAY);
180 }
181
182 @Override
183 public void onClick(View v) {
184 mListener.onInterestClicked(getName());
185 }
186
187 /*
188 * An AsyncTask that downloads an image, formats it then puts it in the give n placeholder.
189 */
190 private static class ImageDownloadTask extends AsyncTask<Void, Void, Drawabl e> {
newt (away) 2015/12/12 00:30:31 This class doesn't logically belong inside Interes
PEConn 2015/12/14 17:05:15 Acknowledged.
191
192 private final String mUrl;
193 private final ImagePlaceholder mImagePlaceholder;
194 private final Resources mResources;
195
196 public ImageDownloadTask(String url, ImagePlaceholder placeholder, Resou rces resources) {
197 mUrl = url;
198 mImagePlaceholder = placeholder;
199 mResources = resources;
200 }
201
202 @Override
203 protected Drawable doInBackground(Void... voids) {
204 // This is run on a background thread.
205 try {
206 // TODO(peconn): Replace this with something from the C++ Chrome stack.
207 URL imageUrl = new URL(mUrl);
208 InputStream in = imageUrl.openStream();
209
210 Bitmap raw = BitmapFactory.decodeStream(in);
211 int dimension = Math.min(raw.getHeight(), raw.getWidth());
212 RoundedBitmapDrawable img = RoundedBitmapDrawableFactory.create( mResources,
213 ThumbnailUtils.extractThumbnail(raw, dimension, dimensio n));
214 img.setCircular(true);
215
216 return img;
217 } catch (IOException e) {
218 Log.e(TAG, "Error downloading image: " + e.toString());
219 }
220 return null;
221 }
222
223 @Override
224 protected void onPostExecute(Drawable image) {
225 // This is run on the main thread.
226 mImagePlaceholder.set(image, mUrl);
227 }
228 }
229
230 /*
231 * A callback class that will set it's parent's image.
232 */
233 private class ImageDownloadedCallback {
234 public void onCallback(Drawable image, String url) {
newt (away) 2015/12/12 00:30:31 I'd call this "onImageDownloaded()"
PEConn 2015/12/14 17:05:15 Done.
235 if (image == null) {
236 return;
237 }
238 // If the Interest this View is displaying has changed while downloa ding, do not update
239 // the image.
240 if (url == mInterest.getImageUrl()) {
241 setImage(image);
242 }
243 }
244 }
245
246 /*
247 * A placeholder for an Image that allows listeners to subscribe to when it is set. It is
248 * like a listenable future that doesn't calculate the value itself. It can only be
249 * accessed on one thread. It can only be set once.
250 */
251 static class ImagePlaceholder {
newt (away) 2015/12/12 00:30:30 How about calling this "ImageHolder"? "ImagePlace
PEConn 2015/12/14 17:05:15 Done.
252 private final List<ImageDownloadedCallback> mListeners = new LinkedList< >();
newt (away) 2015/12/12 00:30:31 "Callback" or "Listener"? Pick one and use it cons
PEConn 2015/12/14 17:05:15 Done.
253 private Drawable mImage = null;
newt (away) 2015/12/12 00:30:31 no need for "= null" in a member variable declarat
PEConn 2015/12/14 17:05:15 Done.
254 private String mUrl;
255
256 public void set(Drawable image, String url) {
257 assert mImage == null;
258 mImage = image;
259 mUrl = url;
260
261 for (ImageDownloadedCallback listener : mListeners) {
newt (away) 2015/12/12 00:30:31 Shouldn't you clear mListeners after calling them?
PEConn 2015/12/14 17:05:15 Done.
262 listener.onCallback(image, mUrl);
263 }
264 }
265
266 public void addListener(ImageDownloadedCallback listener) {
267 if (mImage == null) {
268 mListeners.add(listener);
269 } else {
270 listener.onCallback(mImage, mUrl);
271 }
272 }
273
274 public Boolean isFilled() {
newt (away) 2015/12/12 00:30:31 s/Boolean/boolean/ (Never use Boolean if you can
PEConn 2015/12/14 17:05:15 Done.
275 return mImage != null;
276 }
277
278 public Drawable get() {
newt (away) 2015/12/12 00:30:30 s/get/getImageDrawable/ ?
PEConn 2015/12/14 17:05:15 Done.
279 return mImage;
280 }
281 }
282 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698