1
+ package fr .free .nrw .commons ;
2
+
3
+ import android .net .Uri ;
4
+ import android .os .Looper ;
5
+ import android .os .SystemClock ;
6
+ import com .facebook .imagepipeline .common .BytesRange ;
7
+ import com .facebook .imagepipeline .image .EncodedImage ;
8
+ import com .facebook .imagepipeline .producers .BaseNetworkFetcher ;
9
+ import com .facebook .imagepipeline .producers .BaseProducerContextCallbacks ;
10
+ import com .facebook .imagepipeline .producers .Consumer ;
11
+ import com .facebook .imagepipeline .producers .FetchState ;
12
+ import com .facebook .imagepipeline .producers .NetworkFetcher ;
13
+ import com .facebook .imagepipeline .producers .ProducerContext ;
14
+ import java .io .IOException ;
15
+ import java .util .HashMap ;
16
+ import java .util .Map ;
17
+ import java .util .concurrent .Executor ;
18
+ import okhttp3 .CacheControl ;
19
+ import okhttp3 .Call ;
20
+ import okhttp3 .OkHttpClient ;
21
+ import okhttp3 .Request ;
22
+ import okhttp3 .Response ;
23
+ import okhttp3 .ResponseBody ;
24
+
25
+ /** Network fetcher that uses OkHttp 3 as a backend. */
26
+ public class CustomNetworkFetcher
27
+ extends BaseNetworkFetcher <CustomNetworkFetcher .OkHttpNetworkFetchState > {
28
+
29
+ public static class OkHttpNetworkFetchState extends FetchState {
30
+
31
+ public long submitTime ;
32
+ public long responseTime ;
33
+ public long fetchCompleteTime ;
34
+
35
+ public OkHttpNetworkFetchState (
36
+ Consumer <EncodedImage > consumer , ProducerContext producerContext ) {
37
+ super (consumer , producerContext );
38
+ }
39
+ }
40
+
41
+ private static final String QUEUE_TIME = "queue_time" ;
42
+ private static final String FETCH_TIME = "fetch_time" ;
43
+ private static final String TOTAL_TIME = "total_time" ;
44
+ private static final String IMAGE_SIZE = "image_size" ;
45
+
46
+ private final Call .Factory mCallFactory ;
47
+ private final CacheControl mCacheControl ;
48
+
49
+ private Executor mCancellationExecutor ;
50
+
51
+ /** @param okHttpClient client to use */
52
+ public CustomNetworkFetcher (OkHttpClient okHttpClient ) {
53
+ this (okHttpClient , okHttpClient .dispatcher ().executorService ());
54
+ }
55
+
56
+ /**
57
+ * @param callFactory custom {@link Call.Factory} for fetching image from the network
58
+ * @param cancellationExecutor executor on which fetching cancellation is performed if
59
+ * cancellation is requested from the UI Thread
60
+ */
61
+ public CustomNetworkFetcher (Call .Factory callFactory , Executor cancellationExecutor ) {
62
+ this (callFactory , cancellationExecutor , true );
63
+ }
64
+
65
+ /**
66
+ * @param callFactory custom {@link Call.Factory} for fetching image from the network
67
+ * @param cancellationExecutor executor on which fetching cancellation is performed if
68
+ * cancellation is requested from the UI Thread
69
+ * @param disableOkHttpCache true if network requests should not be cached by OkHttp
70
+ */
71
+ public CustomNetworkFetcher (
72
+ Call .Factory callFactory , Executor cancellationExecutor , boolean disableOkHttpCache ) {
73
+ mCallFactory = callFactory ;
74
+ mCancellationExecutor = cancellationExecutor ;
75
+ mCacheControl = disableOkHttpCache ? new CacheControl .Builder ().noStore ().build () : null ;
76
+ }
77
+
78
+ @ Override
79
+ public OkHttpNetworkFetchState createFetchState (
80
+ Consumer <EncodedImage > consumer , ProducerContext context ) {
81
+ return new OkHttpNetworkFetchState (consumer , context );
82
+ }
83
+
84
+ @ Override
85
+ public void fetch (
86
+ final OkHttpNetworkFetchState fetchState , final NetworkFetcher .Callback callback ) {
87
+ fetchState .submitTime = SystemClock .elapsedRealtime ();
88
+ final Uri uri = fetchState .getUri ();
89
+
90
+ try {
91
+ final Request .Builder requestBuilder = new Request .Builder ().url (uri .toString ()).get ();
92
+
93
+ if (mCacheControl != null ) {
94
+ requestBuilder .cacheControl (mCacheControl );
95
+ }
96
+
97
+ final BytesRange bytesRange = fetchState .getContext ().getImageRequest ().getBytesRange ();
98
+ if (bytesRange != null ) {
99
+ requestBuilder .addHeader ("Range" , bytesRange .toHttpRangeHeaderValue ());
100
+ }
101
+
102
+ fetchWithRequest (fetchState , callback , requestBuilder .build ());
103
+ } catch (Exception e ) {
104
+ // handle error while creating the request
105
+ callback .onFailure (e );
106
+ }
107
+ }
108
+
109
+ @ Override
110
+ public void onFetchCompletion (OkHttpNetworkFetchState fetchState , int byteSize ) {
111
+ fetchState .fetchCompleteTime = SystemClock .elapsedRealtime ();
112
+ }
113
+
114
+ @ Override
115
+ public Map <String , String > getExtraMap (OkHttpNetworkFetchState fetchState , int byteSize ) {
116
+ Map <String , String > extraMap = new HashMap <>(4 );
117
+ extraMap .put (QUEUE_TIME , Long .toString (fetchState .responseTime - fetchState .submitTime ));
118
+ extraMap .put (FETCH_TIME , Long .toString (fetchState .fetchCompleteTime - fetchState .responseTime ));
119
+ extraMap .put (TOTAL_TIME , Long .toString (fetchState .fetchCompleteTime - fetchState .submitTime ));
120
+ extraMap .put (IMAGE_SIZE , Integer .toString (byteSize ));
121
+ return extraMap ;
122
+ }
123
+
124
+ protected void fetchWithRequest (
125
+ final OkHttpNetworkFetchState fetchState ,
126
+ final NetworkFetcher .Callback callback ,
127
+ final Request request ) {
128
+ final Call call = mCallFactory .newCall (request );
129
+
130
+ fetchState
131
+ .getContext ()
132
+ .addCallbacks (
133
+ new BaseProducerContextCallbacks () {
134
+ @ Override
135
+ public void onCancellationRequested () {
136
+ if (Looper .myLooper () != Looper .getMainLooper ()) {
137
+ call .cancel ();
138
+ } else {
139
+ mCancellationExecutor .execute (
140
+ new Runnable () {
141
+ @ Override
142
+ public void run () {
143
+ call .cancel ();
144
+ }
145
+ });
146
+ }
147
+ }
148
+ });
149
+
150
+ call .enqueue (
151
+ new okhttp3 .Callback () {
152
+ @ Override
153
+ public void onResponse (Call call , Response response ) throws IOException {
154
+ fetchState .responseTime = SystemClock .elapsedRealtime ();
155
+ final ResponseBody body = response .body ();
156
+ try {
157
+ if (!response .isSuccessful ()) {
158
+ handleException (
159
+ call , new IOException ("Unexpected HTTP code " + response ), callback );
160
+ return ;
161
+ }
162
+
163
+ BytesRange responseRange =
164
+ BytesRange .fromContentRangeHeader (response .header ("Content-Range" ));
165
+ if (responseRange != null
166
+ && !(responseRange .from == 0
167
+ && responseRange .to == BytesRange .TO_END_OF_CONTENT )) {
168
+ // Only treat as a partial image if the range is not all of the content
169
+ fetchState .setResponseBytesRange (responseRange );
170
+ fetchState .setOnNewResultStatusFlags (Consumer .IS_PARTIAL_RESULT );
171
+ }
172
+
173
+ long contentLength = body .contentLength ();
174
+ if (contentLength < 0 ) {
175
+ contentLength = 0 ;
176
+ }
177
+ callback .onResponse (body .byteStream (), (int ) contentLength );
178
+ } catch (Exception e ) {
179
+ handleException (call , e , callback );
180
+ } finally {
181
+ body .close ();
182
+ }
183
+ }
184
+
185
+ @ Override
186
+ public void onFailure (Call call , IOException e ) {
187
+ handleException (call , e , callback );
188
+ }
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Handles exceptions.
194
+ *
195
+ * <p>OkHttp notifies callers of cancellations via an IOException. If IOException is caught after
196
+ * request cancellation, then the exception is interpreted as successful cancellation and
197
+ * onCancellation is called. Otherwise onFailure is called.
198
+ */
199
+ private void handleException (final Call call , final Exception e , final Callback callback ) {
200
+ if (call .isCanceled ()) {
201
+ callback .onCancellation ();
202
+ } else {
203
+ callback .onFailure (e );
204
+ }
205
+ }
206
+ }
0 commit comments