1/*
2 * Copyright (C) 2015 Square, Inc.
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 */
16package com.squareup.okhttp.recipes;
17
18import com.squareup.okhttp.Interceptor;
19import com.squareup.okhttp.MediaType;
20import com.squareup.okhttp.OkHttpClient;
21import com.squareup.okhttp.Response;
22import com.squareup.okhttp.ResponseBody;
23import com.squareup.okhttp.Request;
24import java.io.IOException;
25import okio.Buffer;
26import okio.BufferedSource;
27import okio.ForwardingSource;
28import okio.Okio;
29import okio.Source;
30
31public final class Progress {
32
33  private final OkHttpClient client = new OkHttpClient();
34
35  public void run() throws Exception {
36    Request request = new Request.Builder()
37        .url("https://publicobject.com/helloworld.txt")
38        .build();
39
40    final ProgressListener progressListener = new ProgressListener() {
41      @Override public void update(long bytesRead, long contentLength, boolean done) {
42        System.out.println(bytesRead);
43        System.out.println(contentLength);
44        System.out.println(done);
45        System.out.format("%d%% done\n", (100 * bytesRead) / contentLength);
46      }
47    };
48
49    client.networkInterceptors().add(new Interceptor() {
50      @Override public Response intercept(Chain chain) throws IOException {
51        Response originalResponse = chain.proceed(chain.request());
52        return originalResponse.newBuilder()
53            .body(new ProgressResponseBody(originalResponse.body(), progressListener))
54            .build();
55        }
56    });
57
58    Response response = client.newCall(request).execute();
59    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
60
61    System.out.println(response.body().string());
62  }
63
64  public static void main(String... args) throws Exception {
65    new Progress().run();
66  }
67
68  private static class ProgressResponseBody extends ResponseBody {
69
70    private final ResponseBody responseBody;
71    private final ProgressListener progressListener;
72    private BufferedSource bufferedSource;
73
74    public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
75      this.responseBody = responseBody;
76      this.progressListener = progressListener;
77    }
78
79    @Override public MediaType contentType() {
80      return responseBody.contentType();
81    }
82
83    @Override public long contentLength() throws IOException {
84      return responseBody.contentLength();
85    }
86
87    @Override public BufferedSource source() throws IOException {
88      if (bufferedSource == null) {
89        bufferedSource = Okio.buffer(source(responseBody.source()));
90      }
91      return bufferedSource;
92    }
93
94    private Source source(Source source) {
95      return new ForwardingSource(source) {
96        long totalBytesRead = 0L;
97        @Override public long read(Buffer sink, long byteCount) throws IOException {
98          long bytesRead = super.read(sink, byteCount);
99          // read() returns the number of bytes read, or -1 if this source is exhausted.
100          totalBytesRead += bytesRead != -1 ? bytesRead : 0;
101          progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
102          return bytesRead;
103        }
104      };
105    }
106  }
107
108  interface ProgressListener {
109    void update(long bytesRead, long contentLength, boolean done);
110  }
111}
112