1+ /*
2+ * Copyright (c) 2014, Parse, LLC. All rights reserved.
3+ *
4+ * You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
5+ * copy, modify, and distribute this software in source code or binary form for use
6+ * in connection with the web services and APIs provided by Parse.
7+ *
8+ * As with any software that integrates with the Parse platform, your use of
9+ * this software is subject to the Parse Terms of Service
10+ * [https://www.parse.com/about/terms]. This copyright notice shall be
11+ * included in all copies or substantial portions of the software.
12+ *
13+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19+ *
20+ */
21+
22+ package com .parse ;
23+
24+ import android .content .Context ;
25+ import android .graphics .Bitmap ;
26+ import android .graphics .BitmapFactory ;
27+ import android .graphics .drawable .Drawable ;
28+ import android .util .AttributeSet ;
29+ import android .widget .ImageView ;
30+
31+ import bolts .Continuation ;
32+ import bolts .Task ;
33+
34+ /**
35+ * A specialized {@code ImageView} that downloads and displays remote images stored on Parse's
36+ * servers.
37+ * <p>
38+ * Given a {@code ParseFile} storing an image, a {@code ParseImageView} works seamlessly to fetch
39+ * the file data and display it in the background. See below for an example:
40+ *
41+ * <pre>
42+ * ParseImageView imageView = (ParseImageView) findViewById(android.R.id.icon);
43+ * // The placeholder will be used before and during the fetch, to be replaced by the fetched image
44+ * // data.
45+ * imageView.setPlaceholder(getResources().getDrawable(R.drawable.placeholder));
46+ * imageView.setParseFile(file);
47+ * imageView.loadInBackground(new GetDataCallback() {
48+ * @Override
49+ * public void done(byte[] data, ParseException e) {
50+ * Log.i("ParseImageView",
51+ * "Fetched! Data length: " + data.length + ", or exception: " + e.getMessage());
52+ * }
53+ * });
54+ * </pre>
55+ */
56+ public class ParseImageView extends ImageView {
57+ private ParseFile file ;
58+ private Drawable placeholder ;
59+ private boolean isLoaded = false ;
60+
61+ /**
62+ * Simple constructor to use when creating a {@code ParseImageView} from code.
63+ *
64+ * @param context
65+ * Context for this View
66+ */
67+ public ParseImageView (Context context ) {
68+ super (context );
69+ }
70+
71+ /**
72+ * Constructor that is called when inflating a {@code ParseImageView} from XML.
73+ *
74+ * @param context
75+ * Context for this View
76+ * @param attributeSet
77+ * AttributeSet defined for this View in XML
78+ */
79+ public ParseImageView (Context context , AttributeSet attributeSet ) {
80+ super (context , attributeSet );
81+ }
82+
83+ /**
84+ * Perform inflation from XML and apply a class-specific base style.
85+ *
86+ * @param context
87+ * Context for this View
88+ * @param attributeSet
89+ * AttributeSet defined for this View in XML
90+ * @param defStyle
91+ * Class-specific base style.
92+ */
93+ public ParseImageView (Context context , AttributeSet attributeSet , int defStyle ) {
94+ super (context , attributeSet , defStyle );
95+ }
96+
97+ @ Override
98+ protected void onDetachedFromWindow () {
99+ // AdapterViews tend to try and avoid calling this, instead preferring to recycle the Views +
100+ // subviews. This is, however, called when the AdapterView itself is detached, or the Activity
101+ // is destroyed.
102+ if (this .file != null ) {
103+ this .file .cancel ();
104+ }
105+ }
106+
107+ @ Override
108+ public void setImageBitmap (Bitmap bitmap ) {
109+ super .setImageBitmap (bitmap );
110+ this .isLoaded = true ;
111+ }
112+
113+ /**
114+ * Sets the placeholder to be used while waiting for an image to be loaded.
115+ *
116+ * @param placeholder
117+ * A {@code Drawable} to be displayed while the remote image data is being fetched. This
118+ * value can be null, and this {@code ImageView} will simply be blank while data is
119+ * fetched.
120+ */
121+ public void setPlaceholder (Drawable placeholder ) {
122+ this .placeholder = placeholder ;
123+ if (!this .isLoaded ) {
124+ this .setImageDrawable (this .placeholder );
125+ }
126+ }
127+
128+ /**
129+ * Sets the remote file on Parse's server that stores the image.
130+ *
131+ * @param file
132+ * The remote file on Parse's server.
133+ */
134+ public void setParseFile (ParseFile file ) {
135+ if (this .file != null ) {
136+ this .file .cancel ();
137+ }
138+ this .isLoaded = false ;
139+ this .file = file ;
140+ this .setImageDrawable (this .placeholder );
141+ }
142+
143+ /**
144+ * Kick off downloading of remote image. When the download is finished, the image data will be
145+ * displayed.
146+ *
147+ * @return A Task that is resolved when the image data is fetched and this View displays the image.
148+ */
149+ public Task <byte []> loadInBackground () {
150+ if (file == null ) {
151+ return Task .forResult (null );
152+ }
153+
154+ final ParseFile loadingFile = file ;
155+ return file .getDataInBackground ().onSuccessTask (new Continuation <byte [], Task <byte []>>() {
156+ @ Override
157+ public Task <byte []> then (Task <byte []> task ) throws Exception {
158+ byte [] data = task .getResult ();
159+ if (file != loadingFile ) {
160+ // This prevents the very slim chance of the file's download finishing and the callback
161+ // triggering just before this ImageView is reused for another ParseObject.
162+ return Task .cancelled ();
163+ }
164+ if (data != null ) {
165+ Bitmap bitmap = BitmapFactory .decodeByteArray (data , 0 , data .length );
166+ if (bitmap != null ) {
167+ setImageBitmap (bitmap );
168+ }
169+ }
170+ return task ;
171+ }
172+ }, Task .UI_THREAD_EXECUTOR );
173+ }
174+
175+ /**
176+ * Kick off downloading of remote image. When the download is finished, the image data will be
177+ * displayed and the {@code completionCallback} will be triggered.
178+ *
179+ * @param completionCallback
180+ * A custom {@code GetDataCallback} to be called after the image data is fetched and this
181+ * {@code ImageView} displays the image.
182+ */
183+ public void loadInBackground (final GetDataCallback completionCallback ) {
184+ ParseTaskUtils .callbackOnMainThreadAsync (loadInBackground (), completionCallback , true );
185+ }
186+ }
0 commit comments