• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
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.android.retaildemo;
18 
19 import android.app.DownloadManager;
20 import android.app.ProgressDialog;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.database.Cursor;
26 import android.net.ConnectivityManager;
27 import android.net.NetworkInfo;
28 import android.net.Uri;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.util.Log;
34 import android.view.ContextThemeWrapper;
35 
36 import java.io.File;
37 import java.io.IOException;
38 import java.net.HttpURLConnection;
39 import java.net.URL;
40 
41 /**
42  * Downloads the video from the specified url. If the video is previously downloaded, then uses
43  * that but checks if there is a more recent version of the video available.
44  */
45 class DownloadVideoTask {
46     private static final String TAG = "DownloadVideoTask";
47     private static final boolean DEBUG = false;
48 
49     private static final int MSG_CHECK_FOR_UPDATE = 1;
50     private static final int MSG_DOWNLOAD_COMPLETE = 2;
51     private static final int MSG_CLEANUP_DOWNLOAD_DIR = 3;
52 
53     private static final int CLEANUP_DELAY_MILLIS = 2 * 1000; // 2 seconds
54 
55     private final Context mContext;
56     private final DownloadManager mDlm;
57     private final File mDownloadFile;
58     private final ResultListener mListener;
59 
60     private Handler mHandler;
61 
62     private ProgressDialog mProgressDialog;
63     private DownloadResultReceiver mDownloadReceiver;
64     private NetworkChangeReceiver mNetworkChangeReceiver;
65     private String mDownloadUrl;
66     private long mVideoDownloadId;
67     private long mVideoUpdateDownloadId;
68     private String mDownloadedPath;
69     private boolean mVideoAlreadySet;
70     private File mPreloadVideoFile;
71 
DownloadVideoTask(Context context, String downloadPath, File preloadVideoFile, ResultListener listener)72     public DownloadVideoTask(Context context, String downloadPath, File preloadVideoFile,
73             ResultListener listener) {
74         mContext = context;
75         mDownloadFile = new File(downloadPath);
76         mListener = listener;
77         mPreloadVideoFile = preloadVideoFile;
78         mDlm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
79         mDownloadUrl = mContext.getString(R.string.retail_demo_video_download_url);
80     }
81 
run()82     public void run() {
83         mDownloadReceiver = new DownloadResultReceiver();
84         mContext.registerReceiver(mDownloadReceiver,
85                 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
86 
87         // Initialize handler
88         HandlerThread thread = new HandlerThread(TAG);
89         thread.start();
90         mHandler = new ThreadHandler(thread.getLooper());
91 
92         mVideoAlreadySet =
93                 mDownloadFile.exists() || mPreloadVideoFile.exists();
94         // If file already exists, no need to download it again.
95         if (mVideoAlreadySet) {
96             if (DEBUG) Log.d(TAG, "Video already exists at either " + mDownloadFile.getPath()
97                     + " or " + mPreloadVideoFile + ", checking for an update... ");
98             mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_FOR_UPDATE));
99         } else {
100             if (!isConnectedToNetwork()) {
101                 mListener.onError();
102                 mNetworkChangeReceiver = new NetworkChangeReceiver();
103                 mContext.registerReceiver(mNetworkChangeReceiver,
104                         new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
105                 return;
106             }
107             startDownload();
108         }
109     }
110 
startDownload()111     private void startDownload() {
112         final DownloadManager.Request request = createDownloadRequest();
113         mVideoDownloadId = mDlm.enqueue(request);
114         if (DEBUG) Log.d(TAG, "Started downloading the video at " + mDownloadUrl
115                 + " to " + mDownloadFile.getPath());
116         showProgressDialog();
117     }
118 
createDownloadRequest()119     private DownloadManager.Request createDownloadRequest() {
120         final DownloadManager.Request request = new DownloadManager.Request(
121                 Uri.parse(mDownloadUrl));
122         request.setDestinationUri(Uri.fromFile(mDownloadFile));
123         return request;
124     }
125 
126     private class DownloadResultReceiver extends BroadcastReceiver {
127         @Override
onReceive(Context context, Intent intent)128         public void onReceive(Context context, Intent intent) {
129             if (!DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
130                 return;
131             }
132 
133             final long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
134             if (id == mVideoDownloadId) {
135                 if (checkDownloadsAndSetVideo(id)) {
136                     mProgressDialog.dismiss();
137                 }
138             } else if (id == mVideoUpdateDownloadId) {
139                 mHandler.sendMessage(mHandler.obtainMessage(MSG_DOWNLOAD_COMPLETE));
140             }
141         }
142     };
143 
144     private final class ThreadHandler extends Handler {
ThreadHandler(Looper looper)145         public ThreadHandler(Looper looper) {
146             super(looper);
147         }
148 
149         @Override
handleMessage(Message msg)150         public void handleMessage(Message msg) {
151             switch (msg.what) {
152                 case MSG_CHECK_FOR_UPDATE:
153                     if (!isConnectedToNetwork()) {
154                         mNetworkChangeReceiver = new NetworkChangeReceiver();
155                         mContext.registerReceiver(mNetworkChangeReceiver,
156                                 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
157                         return;
158                     }
159                     HttpURLConnection conn = null;
160                     try {
161                         conn = (HttpURLConnection) new URL(mDownloadUrl).openConnection();
162                         final long lastModified = mDownloadFile.lastModified();
163                         conn.setIfModifiedSince(lastModified);
164                         conn.connect();
165                         if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
166                             return;
167                         }
168                         final DownloadManager.Request request = createDownloadRequest();
169                         mVideoUpdateDownloadId = mDlm.enqueue(request);
170                         if (DEBUG) Log.d(TAG, "Started downloading the updated video at "
171                                 + mDownloadUrl);
172                     } catch (IOException e) {
173                         Log.e(TAG, "Error while checking for an updated video", e);
174                     } finally {
175                         if (conn != null) {
176                             conn.disconnect();
177                         }
178                     }
179                     break;
180                 case MSG_DOWNLOAD_COMPLETE:
181                     checkDownloadsAndSetVideo(mVideoUpdateDownloadId);
182                     break;
183                 case MSG_CLEANUP_DOWNLOAD_DIR:
184                     // If the video was downloaded to the same location as we needed, then
185                     // nothing else to do.
186                     if (mDownloadFile.getPath().equals(mDownloadedPath)) {
187                         return;
188                     }
189                     if (mDownloadFile.exists()) {
190                         mDownloadFile.delete();
191                     }
192                     if (new File(mDownloadedPath).renameTo(mDownloadFile)) {
193                         mListener.onFileDownloaded(mDownloadFile.getPath());
194                         final String downloadFileName = getFileBaseName(mDownloadFile.getName());
195                         // Delete other files in the directory
196                         for (File file : mDownloadFile.getParentFile().listFiles()) {
197                             if (getFileBaseName(file.getName()).startsWith(downloadFileName)
198                                     && !file.getPath().equals(mDownloadFile.getPath())) {
199                                 file.delete();
200                             }
201                         }
202                     }
203                     break;
204             }
205         }
206     }
207 
checkDownloadsAndSetVideo(long downloadId)208     private boolean checkDownloadsAndSetVideo(long downloadId) {
209         final DownloadManager.Query query =
210                 new DownloadManager.Query().setFilterById(downloadId);
211         Cursor cursor = mDlm.query(query);
212         try {
213             if (cursor != null & cursor.moveToFirst()) {
214                 final int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
215                 if (cursor.getInt(columnIndex) == DownloadManager.STATUS_SUCCESSFUL) {
216                     if (mDownloadReceiver != null) {
217                         mContext.unregisterReceiver(mDownloadReceiver);
218                         mDownloadReceiver = null;
219                     }
220                     final String fileUri = cursor.getString(
221                             cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
222                     mDownloadedPath = Uri.parse(fileUri).getPath();
223                     if (DEBUG) Log.d(TAG, "Video successfully downloaded at " + mDownloadedPath);
224                     mListener.onFileDownloaded(mDownloadedPath);
225                     mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEANUP_DOWNLOAD_DIR),
226                             CLEANUP_DELAY_MILLIS);
227                     return true;
228                 }
229             }
230         } finally {
231             if (cursor != null) {
232                 cursor.close();
233             }
234             if (mNetworkChangeReceiver != null) {
235                 mContext.unregisterReceiver(mNetworkChangeReceiver);
236                 mNetworkChangeReceiver = null;
237             }
238         }
239         return false;
240     }
241 
242     private class NetworkChangeReceiver extends BroadcastReceiver {
243         @Override
onReceive(Context context, Intent intent)244         public void onReceive(Context context, Intent intent) {
245             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())
246                     && isConnectedToNetwork()) {
247                 if (mVideoAlreadySet) {
248                     mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_FOR_UPDATE));
249                 } else {
250                     startDownload();
251                 }
252             }
253         }
254     };
255 
showProgressDialog()256     private void showProgressDialog() {
257         mProgressDialog = new ProgressDialog(
258                 new ContextThemeWrapper(mContext, android.R.style.Theme_Material_Light_Dialog));
259         mProgressDialog.setMessage(mContext.getString(R.string.downloading_video_msg));
260         mProgressDialog.setIndeterminate(false);
261         mProgressDialog.setCancelable(false);
262         mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
263         mProgressDialog.show();
264     }
265 
isConnectedToNetwork()266     private boolean isConnectedToNetwork() {
267         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
268                 Context.CONNECTIVITY_SERVICE);
269         NetworkInfo info = cm.getActiveNetworkInfo();
270         return info != null && info.isConnected();
271     }
272 
getFileBaseName(String fileName)273     private String getFileBaseName(String fileName) {
274         final int pos = fileName.lastIndexOf(".");
275         return pos > 0 ? fileName.substring(0, pos) : fileName;
276     }
277 
278     interface ResultListener {
onFileDownloaded(String downloadedFilePath)279         void onFileDownloaded(String downloadedFilePath);
onError()280         void onError();
281     }
282 }