Statistics
| Branch: | Tag: | Revision:

fdroidclient / F-Droid / src / org / fdroid / fdroid / net / AsyncDownloaderFromAndroid.java @ a09587c7

History | View | Annotate | Download (10.7 KB)

1
package org.fdroid.fdroid.net;
2

    
3
import android.annotation.TargetApi;
4
import android.app.DownloadManager;
5
import android.content.BroadcastReceiver;
6
import android.content.Context;
7
import android.content.Intent;
8
import android.content.IntentFilter;
9
import android.database.Cursor;
10
import android.net.Uri;
11
import android.os.Build;
12
import android.os.ParcelFileDescriptor;
13
import android.text.TextUtils;
14
import android.util.Log;
15

    
16
import org.fdroid.fdroid.Utils;
17

    
18
import java.io.File;
19
import java.io.FileDescriptor;
20
import java.io.FileInputStream;
21
import java.io.FileOutputStream;
22
import java.io.IOException;
23
import java.io.InputStream;
24
import java.io.OutputStream;
25

    
26
/**
27
 * A downloader that uses Android's DownloadManager to perform a download.
28
 */
29
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
30
public class AsyncDownloaderFromAndroid implements AsyncDownloader {
31
    private final Context context;
32
    private final DownloadManager dm;
33
    private File localFile;
34
    private String remoteAddress;
35
    private String downloadTitle;
36
    private String uniqueDownloadId;
37
    private Listener listener;
38

    
39
    private long downloadManagerId = -1;
40

    
41
    /**
42
     * Normally the listener would be provided using a setListener method.
43
     * However for the purposes of this async downloader, it doesn't make
44
     * sense to have an async task without any way to notify the outside
45
     * world about completion. Therefore, we require the listener as a
46
     * parameter to the constructor.
47
     */
48
    public AsyncDownloaderFromAndroid(Context context, Listener listener, String downloadTitle, String downloadId, String remoteAddress, File localFile) {
49
        this.context = context;
50
        this.downloadTitle = downloadTitle;
51
        this.uniqueDownloadId = downloadId;
52
        this.remoteAddress = remoteAddress;
53
        this.listener = listener;
54
        this.localFile = localFile;
55

    
56
        if (TextUtils.isEmpty(downloadTitle)) {
57
            this.downloadTitle = remoteAddress;
58
        }
59

    
60
        dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
61
    }
62

    
63
    @Override
64
    public void download() {
65
        // Check if the download is complete
66
        if ((downloadManagerId = isDownloadComplete(context, uniqueDownloadId)) > 0) {
67
            // clear the notification
68
            dm.remove(downloadManagerId);
69

    
70
            try {
71
                // write the downloaded file to the expected location
72
                ParcelFileDescriptor fd = dm.openDownloadedFile(downloadManagerId);
73
                copyFile(fd.getFileDescriptor(), localFile);
74
                listener.onDownloadComplete();
75
            } catch (IOException e) {
76
                listener.onErrorDownloading(e.getLocalizedMessage());
77
            }
78
            return;
79
        }
80

    
81
        // Check if the download is still in progress
82
        if (downloadManagerId < 0) {
83
            downloadManagerId = isDownloading(context, uniqueDownloadId);
84
        }
85

    
86
        // Start a new download
87
        if (downloadManagerId < 0) {
88
            // set up download request
89
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(remoteAddress));
90
            request.setTitle(downloadTitle);
91
            request.setDescription(uniqueDownloadId); // we will retrieve this later from the description field
92
            this.downloadManagerId = dm.enqueue(request);
93
        }
94

    
95
        context.registerReceiver(receiver,
96
                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
97
    }
98

    
99
    /**
100
     * Copy input file to output file
101
     * @throws IOException
102
     */
103
    private void copyFile(FileDescriptor inputFile, File outputFile) throws IOException {
104
        InputStream input = null;
105
        OutputStream output = null;
106
        try {
107
            input  = new FileInputStream(inputFile);
108
            output = new FileOutputStream(outputFile);
109
            Utils.copy(input, output);
110
        } finally {
111
            Utils.closeQuietly(output);
112
            Utils.closeQuietly(input);
113
        }
114
    }
115

    
116
    @Override
117
    public int getBytesRead() {
118
        if (downloadManagerId < 0) return 0;
119

    
120
        DownloadManager.Query query = new DownloadManager.Query();
121
        query.setFilterById(downloadManagerId);
122
        Cursor c = dm.query(query);
123

    
124
        try {
125
            if (c.moveToFirst()) {
126
                // we use the description column to store the unique id of this download
127
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
128
                return c.getInt(columnIndex);
129
            }
130
        } finally {
131
            c.close();
132
        }
133

    
134
        return 0;
135
    }
136

    
137
    @Override
138
    public int getTotalBytes() {
139
        if (downloadManagerId < 0) return 0;
140

    
141
        DownloadManager.Query query = new DownloadManager.Query();
142
        query.setFilterById(downloadManagerId);
143
        Cursor c = dm.query(query);
144

    
145
        try {
146
            if (c.moveToFirst()) {
147
                // we use the description column to store the unique id for this download
148
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
149
                return c.getInt(columnIndex);
150
            }
151
        } finally {
152
            c.close();
153
        }
154

    
155
        return 0;
156
    }
157

    
158
    @Override
159
    public void attemptCancel(boolean userRequested) {
160
        try {
161
            context.unregisterReceiver(receiver);
162
        } catch (Exception e) {
163
            // ignore if receiver already unregistered
164
        }
165

    
166
        if (userRequested && downloadManagerId >= 0) {
167
            dm.remove(downloadManagerId);
168
        }
169
    }
170

    
171
    /**
172
     * Extract the uniqueDownloadId from a given download id.
173
     * @return - uniqueDownloadId or null if not found
174
     */
175
    public static String getDownloadId(Context context, long downloadId) {
176
        DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
177
        DownloadManager.Query query = new DownloadManager.Query();
178
        query.setFilterById(downloadId);
179
        Cursor c = dm.query(query);
180

    
181
        try {
182
            if (c.moveToFirst()) {
183
                // we use the description column to store the unique id for this download
184
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
185
                return c.getString(columnIndex);
186
            }
187
        } finally {
188
            c.close();
189
        }
190

    
191
        return null;
192
    }
193

    
194
    /**
195
     * Extract the download title from a given download id.
196
     * @return - title or null if not found
197
     */
198
    public static String getDownloadTitle(Context context, long downloadId) {
199
        DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
200
        DownloadManager.Query query = new DownloadManager.Query();
201
        query.setFilterById(downloadId);
202
        Cursor c = dm.query(query);
203

    
204
        try {
205
            if (c.moveToFirst()) {
206
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_TITLE);
207
                return c.getString(columnIndex);
208
            }
209
        } finally {
210
            c.close();
211
        }
212

    
213
        return null;
214
    }
215

    
216
    /**
217
     * Get the downloadManagerId from an Intent sent by the DownloadManagerReceiver
218
     */
219
    public static long getDownloadId(Intent intent) {
220
        if (intent != null) {
221
            if (intent.hasExtra(DownloadManager.EXTRA_DOWNLOAD_ID)) {
222
                // we have been passed a DownloadManager download id, so get the unique id for that download
223
                return intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
224
            }
225

    
226
            if (intent.hasExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS)) {
227
                // we have been passed multiple download id's - just return the first one
228
                long[] downloadIds = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
229
                if (downloadIds != null && downloadIds.length > 0) {
230
                    return downloadIds[0];
231
                }
232
            }
233
        }
234

    
235
        return -1;
236
    }
237

    
238
    /**
239
     * Check if a download is running for the specified id
240
     * @return -1 if not downloading, else the id from the Android download manager
241
     */
242
    public static long isDownloading(Context context, String uniqueDownloadId) {
243
        DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
244
        DownloadManager.Query query = new DownloadManager.Query();
245
        Cursor c = dm.query(query);
246
        int columnUniqueDownloadId = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
247
        int columnId = c.getColumnIndex(DownloadManager.COLUMN_ID);
248

    
249
        try {
250
            while (c.moveToNext()) {
251
                if (uniqueDownloadId.equals(c.getString(columnUniqueDownloadId))) {
252
                    return c.getLong(columnId);
253
                }
254
            }
255
        } finally {
256
            c.close();
257
        }
258

    
259
        return -1;
260
    }
261

    
262
    /**
263
     * Check if a specific download is complete.
264
     * @return -1 if download is not complete, otherwise the download id
265
     */
266
    public static long isDownloadComplete(Context context, String uniqueDownloadId) {
267
        DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
268
        DownloadManager.Query query = new DownloadManager.Query();
269
        query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
270
        Cursor c = dm.query(query);
271
        int columnUniqueDownloadId = c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION);
272
        int columnId = c.getColumnIndex(DownloadManager.COLUMN_ID);
273

    
274
        try {
275
            while (c.moveToNext()) {
276
                if (uniqueDownloadId.equals(c.getString(columnUniqueDownloadId))) {
277
                    return c.getLong(columnId);
278
                }
279
            }
280
        } finally {
281
            c.close();
282
        }
283

    
284
        return -1;
285
    }
286

    
287
    /**
288
     * Broadcast receiver to listen for ACTION_DOWNLOAD_COMPLETE broadcasts
289
     */
290
    BroadcastReceiver receiver = new BroadcastReceiver() {
291
        @Override
292
        public void onReceive(Context context, Intent intent) {
293
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
294
                long dId = getDownloadId(intent);
295
                String downloadId = getDownloadId(context, dId);
296
                if (listener != null && dId == AsyncDownloaderFromAndroid.this.downloadManagerId && downloadId != null) {
297
                    // our current download has just completed, so let's throw up install dialog
298
                    // immediately
299
                    try {
300
                        context.unregisterReceiver(receiver);
301
                    } catch (Exception e) {
302
                        // ignore if receiver already unregistered
303
                    }
304

    
305
                    // call download() to copy the file and start the installer
306
                    download();
307
                }
308
            }
309
        }
310
    };
311
}