| 14792 |
manas |
1 |
/**
|
|
|
2 |
* Copyright 2010-present Facebook.
|
|
|
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.facebook.widget;
|
|
|
18 |
|
|
|
19 |
import android.content.Context;
|
|
|
20 |
import android.content.res.TypedArray;
|
|
|
21 |
import android.graphics.Bitmap;
|
|
|
22 |
import android.graphics.BitmapFactory;
|
|
|
23 |
import android.os.Bundle;
|
|
|
24 |
import android.os.Parcelable;
|
|
|
25 |
import android.util.AttributeSet;
|
|
|
26 |
import android.util.Log;
|
|
|
27 |
import android.view.ViewGroup;
|
|
|
28 |
import android.widget.FrameLayout;
|
|
|
29 |
import android.widget.ImageView;
|
|
|
30 |
import com.facebook.FacebookException;
|
|
|
31 |
import com.facebook.LoggingBehavior;
|
|
|
32 |
import com.facebook.android.R;
|
|
|
33 |
import com.facebook.internal.*;
|
|
|
34 |
|
|
|
35 |
import java.net.URISyntaxException;
|
|
|
36 |
|
|
|
37 |
/**
|
|
|
38 |
* View that displays the profile photo of a supplied profile ID, while conforming
|
|
|
39 |
* to user specified dimensions.
|
|
|
40 |
*/
|
|
|
41 |
public class ProfilePictureView extends FrameLayout {
|
|
|
42 |
|
|
|
43 |
/**
|
|
|
44 |
* Callback interface that will be called when a network or other error is encountered
|
|
|
45 |
* while retrieving profile pictures.
|
|
|
46 |
*/
|
|
|
47 |
public interface OnErrorListener {
|
|
|
48 |
/**
|
|
|
49 |
* Called when a network or other error is encountered.
|
|
|
50 |
* @param error a FacebookException representing the error that was encountered.
|
|
|
51 |
*/
|
|
|
52 |
void onError(FacebookException error);
|
|
|
53 |
}
|
|
|
54 |
|
|
|
55 |
/**
|
|
|
56 |
* Tag used when logging calls are made by ProfilePictureView
|
|
|
57 |
*/
|
|
|
58 |
public static final String TAG = ProfilePictureView.class.getSimpleName();
|
|
|
59 |
|
|
|
60 |
/**
|
|
|
61 |
* Indicates that the specific size of the View will be set via layout params.
|
|
|
62 |
* ProfilePictureView will default to NORMAL X NORMAL, if the layout params set on
|
|
|
63 |
* this instance do not have a fixed size.
|
|
|
64 |
* Used in calls to setPresetSize() and getPresetSize().
|
|
|
65 |
* Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
|
|
|
66 |
*/
|
|
|
67 |
public static final int CUSTOM = -1;
|
|
|
68 |
|
|
|
69 |
/**
|
|
|
70 |
* Indicates that the profile image should fit in a SMALL X SMALL space, regardless
|
|
|
71 |
* of whether the cropped or un-cropped version is chosen.
|
|
|
72 |
* Used in calls to setPresetSize() and getPresetSize().
|
|
|
73 |
* Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
|
|
|
74 |
*/
|
|
|
75 |
public static final int SMALL = -2;
|
|
|
76 |
|
|
|
77 |
/**
|
|
|
78 |
* Indicates that the profile image should fit in a NORMAL X NORMAL space, regardless
|
|
|
79 |
* of whether the cropped or un-cropped version is chosen.
|
|
|
80 |
* Used in calls to setPresetSize() and getPresetSize().
|
|
|
81 |
* Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
|
|
|
82 |
*/
|
|
|
83 |
public static final int NORMAL = -3;
|
|
|
84 |
|
|
|
85 |
/**
|
|
|
86 |
* Indicates that the profile image should fit in a LARGE X LARGE space, regardless
|
|
|
87 |
* of whether the cropped or un-cropped version is chosen.
|
|
|
88 |
* Used in calls to setPresetSize() and getPresetSize().
|
|
|
89 |
* Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
|
|
|
90 |
*/
|
|
|
91 |
public static final int LARGE = -4;
|
|
|
92 |
|
|
|
93 |
private static final int MIN_SIZE = 1;
|
|
|
94 |
private static final boolean IS_CROPPED_DEFAULT_VALUE = true;
|
|
|
95 |
private static final String SUPER_STATE_KEY = "ProfilePictureView_superState";
|
|
|
96 |
private static final String PROFILE_ID_KEY = "ProfilePictureView_profileId";
|
|
|
97 |
private static final String PRESET_SIZE_KEY = "ProfilePictureView_presetSize";
|
|
|
98 |
private static final String IS_CROPPED_KEY = "ProfilePictureView_isCropped";
|
|
|
99 |
private static final String BITMAP_KEY = "ProfilePictureView_bitmap";
|
|
|
100 |
private static final String BITMAP_WIDTH_KEY = "ProfilePictureView_width";
|
|
|
101 |
private static final String BITMAP_HEIGHT_KEY = "ProfilePictureView_height";
|
|
|
102 |
private static final String PENDING_REFRESH_KEY = "ProfilePictureView_refresh";
|
|
|
103 |
|
|
|
104 |
private String profileId;
|
|
|
105 |
private int queryHeight = ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
106 |
private int queryWidth = ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
107 |
private boolean isCropped = IS_CROPPED_DEFAULT_VALUE;
|
|
|
108 |
private Bitmap imageContents;
|
|
|
109 |
private ImageView image;
|
|
|
110 |
private int presetSizeType = CUSTOM;
|
|
|
111 |
private ImageRequest lastRequest;
|
|
|
112 |
private OnErrorListener onErrorListener;
|
|
|
113 |
private Bitmap customizedDefaultProfilePicture = null;
|
|
|
114 |
|
|
|
115 |
/**
|
|
|
116 |
* Constructor
|
|
|
117 |
*
|
|
|
118 |
* @param context Context for this View
|
|
|
119 |
*/
|
|
|
120 |
public ProfilePictureView(Context context) {
|
|
|
121 |
super(context);
|
|
|
122 |
initialize(context);
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
/**
|
|
|
126 |
* Constructor
|
|
|
127 |
*
|
|
|
128 |
* @param context Context for this View
|
|
|
129 |
* @param attrs AttributeSet for this View.
|
|
|
130 |
* The attribute 'preset_size' is processed here
|
|
|
131 |
*/
|
|
|
132 |
public ProfilePictureView(Context context, AttributeSet attrs) {
|
|
|
133 |
super(context, attrs);
|
|
|
134 |
initialize(context);
|
|
|
135 |
parseAttributes(attrs);
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
/**
|
|
|
139 |
* Constructor
|
|
|
140 |
*
|
|
|
141 |
* @param context Context for this View
|
|
|
142 |
* @param attrs AttributeSet for this View.
|
|
|
143 |
* The attribute 'preset_size' is processed here
|
|
|
144 |
* @param defStyle Default style for this View
|
|
|
145 |
*/
|
|
|
146 |
public ProfilePictureView(Context context, AttributeSet attrs, int defStyle) {
|
|
|
147 |
super(context, attrs, defStyle);
|
|
|
148 |
initialize(context);
|
|
|
149 |
parseAttributes(attrs);
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
/**
|
|
|
153 |
* Gets the current preset size type
|
|
|
154 |
*
|
|
|
155 |
* @return The current preset size type, if set; CUSTOM if not
|
|
|
156 |
*/
|
|
|
157 |
public final int getPresetSize() {
|
|
|
158 |
return presetSizeType;
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
/**
|
|
|
162 |
* Apply a preset size to this profile photo
|
|
|
163 |
*
|
|
|
164 |
* @param sizeType The size type to apply: SMALL, NORMAL or LARGE
|
|
|
165 |
*/
|
|
|
166 |
public final void setPresetSize(int sizeType) {
|
|
|
167 |
switch (sizeType) {
|
|
|
168 |
case SMALL:
|
|
|
169 |
case NORMAL:
|
|
|
170 |
case LARGE:
|
|
|
171 |
case CUSTOM:
|
|
|
172 |
this.presetSizeType = sizeType;
|
|
|
173 |
break;
|
|
|
174 |
|
|
|
175 |
default:
|
|
|
176 |
throw new IllegalArgumentException("Must use a predefined preset size");
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
requestLayout();
|
|
|
180 |
}
|
|
|
181 |
|
|
|
182 |
/**
|
|
|
183 |
* Indicates whether the cropped version of the profile photo has been chosen
|
|
|
184 |
*
|
|
|
185 |
* @return True if the cropped version is chosen, false if not.
|
|
|
186 |
*/
|
|
|
187 |
public final boolean isCropped() {
|
|
|
188 |
return isCropped;
|
|
|
189 |
}
|
|
|
190 |
|
|
|
191 |
/**
|
|
|
192 |
* Sets the profile photo to be the cropped version, or the original version
|
|
|
193 |
*
|
|
|
194 |
* @param showCroppedVersion True to select the cropped version
|
|
|
195 |
* False to select the standard version
|
|
|
196 |
*/
|
|
|
197 |
public final void setCropped(boolean showCroppedVersion) {
|
|
|
198 |
isCropped = showCroppedVersion;
|
|
|
199 |
// No need to force the refresh since we will catch the change in required dimensions
|
|
|
200 |
refreshImage(false);
|
|
|
201 |
}
|
|
|
202 |
|
|
|
203 |
/**
|
|
|
204 |
* Returns the profile Id for the current profile photo
|
|
|
205 |
*
|
|
|
206 |
* @return The profile Id
|
|
|
207 |
*/
|
|
|
208 |
public final String getProfileId() {
|
|
|
209 |
return profileId;
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
/**
|
|
|
213 |
* Sets the profile Id for this profile photo
|
|
|
214 |
*
|
|
|
215 |
* @param profileId The profileId
|
|
|
216 |
* NULL/Empty String will show the blank profile photo
|
|
|
217 |
*/
|
|
|
218 |
public final void setProfileId(String profileId) {
|
|
|
219 |
boolean force = false;
|
|
|
220 |
if (Utility.isNullOrEmpty(this.profileId) || !this.profileId.equalsIgnoreCase(profileId)) {
|
|
|
221 |
// Clear out the old profilePicture before requesting for the new one.
|
|
|
222 |
setBlankProfilePicture();
|
|
|
223 |
force = true;
|
|
|
224 |
}
|
|
|
225 |
|
|
|
226 |
this.profileId = profileId;
|
|
|
227 |
refreshImage(force);
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
/**
|
|
|
231 |
* Returns the current OnErrorListener for this instance of ProfilePictureView
|
|
|
232 |
*
|
|
|
233 |
* @return The OnErrorListener
|
|
|
234 |
*/
|
|
|
235 |
public final OnErrorListener getOnErrorListener() {
|
|
|
236 |
return onErrorListener;
|
|
|
237 |
}
|
|
|
238 |
|
|
|
239 |
/**
|
|
|
240 |
* Sets an OnErrorListener for this instance of ProfilePictureView to call into when
|
|
|
241 |
* certain exceptions occur.
|
|
|
242 |
*
|
|
|
243 |
* @param onErrorListener The Listener object to set
|
|
|
244 |
*/
|
|
|
245 |
public final void setOnErrorListener(OnErrorListener onErrorListener) {
|
|
|
246 |
this.onErrorListener = onErrorListener;
|
|
|
247 |
}
|
|
|
248 |
|
|
|
249 |
/**
|
|
|
250 |
* The ProfilePictureView will display the provided image while the specified
|
|
|
251 |
* profile is being loaded, or if the specified profile is not available.
|
|
|
252 |
*
|
|
|
253 |
* @param inputBitmap The bitmap to render until the actual profile is loaded.
|
|
|
254 |
*/
|
|
|
255 |
public final void setDefaultProfilePicture(Bitmap inputBitmap) {
|
|
|
256 |
customizedDefaultProfilePicture = inputBitmap;
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
/**
|
|
|
260 |
* Overriding onMeasure to handle the case where WRAP_CONTENT might be
|
|
|
261 |
* specified in the layout. Since we don't know the dimensions of the profile
|
|
|
262 |
* photo, we need to handle this case specifically.
|
|
|
263 |
* <p/>
|
|
|
264 |
* The approach is to default to a NORMAL sized amount of space in the case that
|
|
|
265 |
* a preset size is not specified. This logic is applied to both width and height
|
|
|
266 |
*/
|
|
|
267 |
@Override
|
|
|
268 |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
|
269 |
ViewGroup.LayoutParams params = getLayoutParams();
|
|
|
270 |
boolean customMeasure = false;
|
|
|
271 |
int newHeight = MeasureSpec.getSize(heightMeasureSpec);
|
|
|
272 |
int newWidth = MeasureSpec.getSize(widthMeasureSpec);
|
|
|
273 |
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY &&
|
|
|
274 |
params.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
|
275 |
newHeight = getPresetSizeInPixels(true); // Default to a preset size
|
|
|
276 |
heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
|
|
|
277 |
customMeasure = true;
|
|
|
278 |
}
|
|
|
279 |
|
|
|
280 |
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY &&
|
|
|
281 |
params.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
|
282 |
newWidth = getPresetSizeInPixels(true); // Default to a preset size
|
|
|
283 |
widthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
|
|
|
284 |
customMeasure = true;
|
|
|
285 |
}
|
|
|
286 |
|
|
|
287 |
if (customMeasure) {
|
|
|
288 |
// Since we are providing custom dimensions, we need to handle the measure
|
|
|
289 |
// phase from here
|
|
|
290 |
setMeasuredDimension(newWidth, newHeight);
|
|
|
291 |
measureChildren(widthMeasureSpec, heightMeasureSpec);
|
|
|
292 |
} else {
|
|
|
293 |
// Rely on FrameLayout to do the right thing
|
|
|
294 |
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
|
295 |
}
|
|
|
296 |
}
|
|
|
297 |
|
|
|
298 |
/**
|
|
|
299 |
* In addition to calling super.Layout(), we also attempt to get a new image that
|
|
|
300 |
* is properly size for the layout dimensions
|
|
|
301 |
*/
|
|
|
302 |
@Override
|
|
|
303 |
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
|
304 |
super.onLayout(changed, left, top, right, bottom);
|
|
|
305 |
|
|
|
306 |
// See if the image needs redrawing
|
|
|
307 |
refreshImage(false);
|
|
|
308 |
}
|
|
|
309 |
|
|
|
310 |
/**
|
|
|
311 |
* Some of the current state is returned as a Bundle to allow quick restoration
|
|
|
312 |
* of the ProfilePictureView object in scenarios like orientation changes.
|
|
|
313 |
* @return a Parcelable containing the current state
|
|
|
314 |
*/
|
|
|
315 |
@Override
|
|
|
316 |
protected Parcelable onSaveInstanceState() {
|
|
|
317 |
Parcelable superState = super.onSaveInstanceState();
|
|
|
318 |
Bundle instanceState = new Bundle();
|
|
|
319 |
instanceState.putParcelable(SUPER_STATE_KEY, superState);
|
|
|
320 |
instanceState.putString(PROFILE_ID_KEY, profileId);
|
|
|
321 |
instanceState.putInt(PRESET_SIZE_KEY, presetSizeType);
|
|
|
322 |
instanceState.putBoolean(IS_CROPPED_KEY, isCropped);
|
|
|
323 |
instanceState.putParcelable(BITMAP_KEY, imageContents);
|
|
|
324 |
instanceState.putInt(BITMAP_WIDTH_KEY, queryWidth);
|
|
|
325 |
instanceState.putInt(BITMAP_HEIGHT_KEY, queryHeight);
|
|
|
326 |
instanceState.putBoolean(PENDING_REFRESH_KEY, lastRequest != null);
|
|
|
327 |
|
|
|
328 |
return instanceState;
|
|
|
329 |
}
|
|
|
330 |
|
|
|
331 |
/**
|
|
|
332 |
* If the passed in state is a Bundle, an attempt is made to restore from it.
|
|
|
333 |
* @param state a Parcelable containing the current state
|
|
|
334 |
*/
|
|
|
335 |
@Override
|
|
|
336 |
protected void onRestoreInstanceState(Parcelable state) {
|
|
|
337 |
if (state.getClass() != Bundle.class) {
|
|
|
338 |
super.onRestoreInstanceState(state);
|
|
|
339 |
} else {
|
|
|
340 |
Bundle instanceState = (Bundle)state;
|
|
|
341 |
super.onRestoreInstanceState(instanceState.getParcelable(SUPER_STATE_KEY));
|
|
|
342 |
|
|
|
343 |
profileId = instanceState.getString(PROFILE_ID_KEY);
|
|
|
344 |
presetSizeType = instanceState.getInt(PRESET_SIZE_KEY);
|
|
|
345 |
isCropped = instanceState.getBoolean(IS_CROPPED_KEY);
|
|
|
346 |
queryWidth = instanceState.getInt(BITMAP_WIDTH_KEY);
|
|
|
347 |
queryHeight = instanceState.getInt(BITMAP_HEIGHT_KEY);
|
|
|
348 |
|
|
|
349 |
setImageBitmap((Bitmap)instanceState.getParcelable(BITMAP_KEY));
|
|
|
350 |
|
|
|
351 |
if (instanceState.getBoolean(PENDING_REFRESH_KEY)) {
|
|
|
352 |
refreshImage(true);
|
|
|
353 |
}
|
|
|
354 |
}
|
|
|
355 |
}
|
|
|
356 |
|
|
|
357 |
@Override
|
|
|
358 |
protected void onDetachedFromWindow() {
|
|
|
359 |
super.onDetachedFromWindow();
|
|
|
360 |
|
|
|
361 |
// Null out lastRequest. This way, when the response is returned, we can ascertain
|
|
|
362 |
// that the view is detached and hence should not attempt to update its contents.
|
|
|
363 |
lastRequest = null;
|
|
|
364 |
}
|
|
|
365 |
|
|
|
366 |
private void initialize(Context context) {
|
|
|
367 |
// We only want our ImageView in here. Nothing else is permitted
|
|
|
368 |
removeAllViews();
|
|
|
369 |
|
|
|
370 |
image = new ImageView(context);
|
|
|
371 |
|
|
|
372 |
LayoutParams imageLayout = new LayoutParams(
|
|
|
373 |
LayoutParams.MATCH_PARENT,
|
|
|
374 |
LayoutParams.MATCH_PARENT);
|
|
|
375 |
|
|
|
376 |
image.setLayoutParams(imageLayout);
|
|
|
377 |
|
|
|
378 |
// We want to prevent up-scaling the image, but still have it fit within
|
|
|
379 |
// the layout bounds as best as possible.
|
|
|
380 |
image.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
|
|
381 |
addView(image);
|
|
|
382 |
}
|
|
|
383 |
|
|
|
384 |
private void parseAttributes(AttributeSet attrs) {
|
|
|
385 |
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.com_facebook_profile_picture_view);
|
|
|
386 |
setPresetSize(a.getInt(R.styleable.com_facebook_profile_picture_view_preset_size, CUSTOM));
|
|
|
387 |
isCropped = a.getBoolean(R.styleable.com_facebook_profile_picture_view_is_cropped, IS_CROPPED_DEFAULT_VALUE);
|
|
|
388 |
a.recycle();
|
|
|
389 |
}
|
|
|
390 |
|
|
|
391 |
private void refreshImage(boolean force) {
|
|
|
392 |
boolean changed = updateImageQueryParameters();
|
|
|
393 |
// Note: do not use Utility.isNullOrEmpty here as this will cause the Eclipse
|
|
|
394 |
// Graphical Layout editor to fail in some cases
|
|
|
395 |
if (profileId == null || profileId.length() == 0 ||
|
|
|
396 |
((queryWidth == ImageRequest.UNSPECIFIED_DIMENSION) &&
|
|
|
397 |
(queryHeight == ImageRequest.UNSPECIFIED_DIMENSION))) {
|
|
|
398 |
setBlankProfilePicture();
|
|
|
399 |
} else if (changed || force) {
|
|
|
400 |
sendImageRequest(true);
|
|
|
401 |
}
|
|
|
402 |
}
|
|
|
403 |
|
|
|
404 |
private void setBlankProfilePicture() {
|
|
|
405 |
if (customizedDefaultProfilePicture == null) {
|
|
|
406 |
int blankImageResource = isCropped() ?
|
|
|
407 |
R.drawable.com_facebook_profile_picture_blank_square :
|
|
|
408 |
R.drawable.com_facebook_profile_picture_blank_portrait;
|
|
|
409 |
setImageBitmap( BitmapFactory.decodeResource(getResources(), blankImageResource));
|
|
|
410 |
} else {
|
|
|
411 |
// Update profile image dimensions.
|
|
|
412 |
updateImageQueryParameters();
|
|
|
413 |
// Resize inputBitmap to new dimensions of queryWidth and queryHeight.
|
|
|
414 |
Bitmap scaledBitmap = Bitmap.createScaledBitmap(customizedDefaultProfilePicture, queryWidth, queryHeight, false);
|
|
|
415 |
setImageBitmap(scaledBitmap);
|
|
|
416 |
}
|
|
|
417 |
}
|
|
|
418 |
|
|
|
419 |
private void setImageBitmap(Bitmap imageBitmap) {
|
|
|
420 |
if (image != null && imageBitmap != null) {
|
|
|
421 |
imageContents = imageBitmap; // Hold for save-restore cycles
|
|
|
422 |
image.setImageBitmap(imageBitmap);
|
|
|
423 |
}
|
|
|
424 |
}
|
|
|
425 |
|
|
|
426 |
private void sendImageRequest(boolean allowCachedResponse) {
|
|
|
427 |
try {
|
|
|
428 |
ImageRequest.Builder requestBuilder = new ImageRequest.Builder(
|
|
|
429 |
getContext(),
|
|
|
430 |
ImageRequest.getProfilePictureUrl(profileId, queryWidth, queryHeight));
|
|
|
431 |
|
|
|
432 |
ImageRequest request = requestBuilder.setAllowCachedRedirects(allowCachedResponse)
|
|
|
433 |
.setCallerTag(this)
|
|
|
434 |
.setCallback(
|
|
|
435 |
new ImageRequest.Callback() {
|
|
|
436 |
@Override
|
|
|
437 |
public void onCompleted(ImageResponse response) {
|
|
|
438 |
processResponse(response);
|
|
|
439 |
}
|
|
|
440 |
})
|
|
|
441 |
.build();
|
|
|
442 |
|
|
|
443 |
// Make sure to cancel the old request before sending the new one to prevent
|
|
|
444 |
// accidental cancellation of the new request. This could happen if the URL and
|
|
|
445 |
// caller tag stayed the same.
|
|
|
446 |
if (lastRequest != null) {
|
|
|
447 |
ImageDownloader.cancelRequest(lastRequest);
|
|
|
448 |
}
|
|
|
449 |
lastRequest = request;
|
|
|
450 |
|
|
|
451 |
ImageDownloader.downloadAsync(request);
|
|
|
452 |
} catch (URISyntaxException e) {
|
|
|
453 |
Logger.log(LoggingBehavior.REQUESTS, Log.ERROR, TAG, e.toString());
|
|
|
454 |
}
|
|
|
455 |
}
|
|
|
456 |
|
|
|
457 |
private void processResponse(ImageResponse response) {
|
|
|
458 |
// First check if the response is for the right request. We may have:
|
|
|
459 |
// 1. Sent a new request, thus super-ceding this one.
|
|
|
460 |
// 2. Detached this view, in which case the response should be discarded.
|
|
|
461 |
if (response.getRequest() == lastRequest) {
|
|
|
462 |
lastRequest = null;
|
|
|
463 |
Bitmap responseImage = response.getBitmap();
|
|
|
464 |
Exception error = response.getError();
|
|
|
465 |
if (error != null) {
|
|
|
466 |
OnErrorListener listener = onErrorListener;
|
|
|
467 |
if (listener != null) {
|
|
|
468 |
listener.onError(new FacebookException(
|
|
|
469 |
"Error in downloading profile picture for profileId: " + getProfileId(), error));
|
|
|
470 |
} else {
|
|
|
471 |
Logger.log(LoggingBehavior.REQUESTS, Log.ERROR, TAG, error.toString());
|
|
|
472 |
}
|
|
|
473 |
} else if (responseImage != null) {
|
|
|
474 |
setImageBitmap(responseImage);
|
|
|
475 |
|
|
|
476 |
if (response.isCachedRedirect()) {
|
|
|
477 |
sendImageRequest(false);
|
|
|
478 |
}
|
|
|
479 |
}
|
|
|
480 |
}
|
|
|
481 |
}
|
|
|
482 |
|
|
|
483 |
private boolean updateImageQueryParameters() {
|
|
|
484 |
int newHeightPx = getHeight();
|
|
|
485 |
int newWidthPx = getWidth();
|
|
|
486 |
if (newWidthPx < MIN_SIZE || newHeightPx < MIN_SIZE) {
|
|
|
487 |
// Not enough space laid out for this View yet. Or something else is awry.
|
|
|
488 |
return false;
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
int presetSize = getPresetSizeInPixels(false);
|
|
|
492 |
if (presetSize != ImageRequest.UNSPECIFIED_DIMENSION) {
|
|
|
493 |
newWidthPx = presetSize;
|
|
|
494 |
newHeightPx = presetSize;
|
|
|
495 |
}
|
|
|
496 |
|
|
|
497 |
// The cropped version is square
|
|
|
498 |
// If full version is desired, then only one dimension is required.
|
|
|
499 |
if (newWidthPx <= newHeightPx) {
|
|
|
500 |
newHeightPx = isCropped() ? newWidthPx : ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
501 |
} else {
|
|
|
502 |
newWidthPx = isCropped() ? newHeightPx : ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
503 |
}
|
|
|
504 |
|
|
|
505 |
boolean changed = (newWidthPx != queryWidth) || (newHeightPx != queryHeight);
|
|
|
506 |
|
|
|
507 |
queryWidth = newWidthPx;
|
|
|
508 |
queryHeight = newHeightPx;
|
|
|
509 |
|
|
|
510 |
return changed;
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
private int getPresetSizeInPixels(boolean forcePreset) {
|
|
|
514 |
int dimensionId;
|
|
|
515 |
switch (presetSizeType) {
|
|
|
516 |
case SMALL:
|
|
|
517 |
dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_small;
|
|
|
518 |
break;
|
|
|
519 |
case NORMAL:
|
|
|
520 |
dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_normal;
|
|
|
521 |
break;
|
|
|
522 |
case LARGE:
|
|
|
523 |
dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_large;
|
|
|
524 |
break;
|
|
|
525 |
case CUSTOM:
|
|
|
526 |
if (!forcePreset) {
|
|
|
527 |
return ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
528 |
} else {
|
|
|
529 |
dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_normal;
|
|
|
530 |
break;
|
|
|
531 |
}
|
|
|
532 |
default:
|
|
|
533 |
return ImageRequest.UNSPECIFIED_DIMENSION;
|
|
|
534 |
}
|
|
|
535 |
|
|
|
536 |
return getResources().getDimensionPixelSize(dimensionId);
|
|
|
537 |
}
|
|
|
538 |
}
|