Update download functionality.

- Check if the video has been updated.
- If there is no network, listen for connectivity changes.

Bug: 29359781
Change-Id: Iae57d609be90316d3d7593316bcf30d10ca6cd89
diff --git a/src/com/android/retaildemo/DemoPlayer.java b/src/com/android/retaildemo/DemoPlayer.java
index 9597335..5447432 100644
--- a/src/com/android/retaildemo/DemoPlayer.java
+++ b/src/com/android/retaildemo/DemoPlayer.java
@@ -17,18 +17,9 @@
 package com.android.retaildemo;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DownloadManager;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
 import android.media.MediaPlayer;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
@@ -49,23 +40,20 @@
  * Note: this activity checks for the file retail_video.mp4 in the path specified by the system
  * property "ro.retaildemo.video_path". To run this, push your test video to that path.
  */
-public class DemoPlayer extends Activity {
+public class DemoPlayer extends Activity implements DownloadVideoTask.ResultListener {
 
     private static final String TAG = "DemoPlayer";
+    private static final boolean DEBUG = false;
 
     private static final String VIDEO_FILE_NAME = "retail_demo.mp4";
     private static final String PRELOADED_VIDEO_FILE = Environment.getDataPreloadsDemoDirectory()
             + File.separator + VIDEO_FILE_NAME;
 
     private PowerManager mPowerManager;
-    private DownloadManager mDlm;
-
-    private ProgressDialog mProgressDialog;
 
     private VideoView mVideoView;
     private int mVideoPosition;
-    private long mVideoDownloadId;
-    private String mDownloadedPath;
+    private String mDownloadPath;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -88,9 +76,8 @@
         setContentView(R.layout.retail_video);
 
         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
-        mDlm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
 
-        mDownloadedPath = getObbDir().getPath() + File.separator + VIDEO_FILE_NAME;
+        mDownloadPath = getObbDir().getPath() + File.separator + VIDEO_FILE_NAME;
         mVideoView = (VideoView) findViewById(R.id.video_content);
 
         // Start playing the video when it is ready
@@ -112,14 +99,24 @@
     private void loadVideo() {
         // If the video is preloaded, then use that. Otherwise download it from the specified url.
         if (new File(PRELOADED_VIDEO_FILE).exists()) {
-            Log.i(TAG, "Using the preloaded video at " + PRELOADED_VIDEO_FILE);
+            if (DEBUG) Log.d(TAG, "Using the preloaded video at " + PRELOADED_VIDEO_FILE);
             setVideoPath(PRELOADED_VIDEO_FILE);
         } else {
-            downloadVideo();
+            new DownloadVideoTask(this, mDownloadPath, this).run();
         }
     }
 
     @Override
+    public void onFileDownloaded(final String filePath) {
+        mVideoView.post(new Runnable() {
+            @Override
+            public void run() {
+                mVideoView.setVideoPath(filePath);
+            }
+        });
+    }
+
+    @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (getSystemService(UserManager.class).isDemoUser()) {
             disableSelf();
@@ -175,46 +172,6 @@
         }
     }
 
-    private void downloadVideo() {
-        File downloadedFile = new File(mDownloadedPath);
-        if (downloadedFile.exists()) {
-            Log.i(TAG, "Using the alreaded downloaded video at " + mDownloadedPath);
-            // File already exists, no need to download it again.
-            setVideoPath(mDownloadedPath);
-            return;
-        }
-        if (!isConnectedToNetwork()) {
-            showErrorMsgDialog(R.string.no_network_connectivity);
-            return;
-        }
-
-        showProgressDialog();
-        final String location = getString(R.string.retail_demo_video_download_url);
-        final DownloadManager.Request request = new DownloadManager.Request(
-                Uri.parse(location));
-        request.setDestinationUri(Uri.fromFile(downloadedFile));
-        registerReceiver(mDownloadReceiver,
-                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
-        mVideoDownloadId = mDlm.enqueue(request);
-        Log.i(TAG, "Started downloading the video at " + mDownloadedPath);
-    }
-
-    private void showProgressDialog() {
-        mProgressDialog = new ProgressDialog(this);
-        mProgressDialog.setMessage(getString(R.string.downloading_video_msg));
-        mProgressDialog.setIndeterminate(false);
-        mProgressDialog.setCancelable(false);
-        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
-        mProgressDialog.show();
-    }
-
-    private void showErrorMsgDialog(int msgResId) {
-        new AlertDialog.Builder(this)
-                .setMessage(msgResId)
-                .setCancelable(false)
-                .show();
-    }
-
     private void forceTurnOnScreen() {
         final PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(
                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
@@ -222,41 +179,4 @@
         // Device waken up, release the wake-lock
         wakeLock.release();
     }
-
-    private boolean isConnectedToNetwork() {
-        ConnectivityManager cm = (ConnectivityManager) getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        NetworkInfo info = cm.getActiveNetworkInfo();
-        return info != null && info.isConnected();
-    }
-
-    private BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
-                return;
-            }
-            final DownloadManager.Query query =
-                    new DownloadManager.Query().setFilterById(mVideoDownloadId);
-            Cursor cursor = mDlm.query(query);
-            try {
-                if (cursor != null & cursor.moveToFirst()) {
-                    final int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
-                    if (cursor.getInt(columnIndex) == DownloadManager.STATUS_SUCCESSFUL) {
-                        unregisterReceiver(mDownloadReceiver);
-                        // TODO: Persist the downloaded location to use it next time.
-                        String fileUri = cursor.getString(
-                                cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
-                        Log.i(TAG, "Video successfully downloaded at " + fileUri);
-                        mVideoView.setVideoURI(Uri.parse(fileUri));
-                        mProgressDialog.dismiss();
-                    }
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-    };
 }
diff --git a/src/com/android/retaildemo/DownloadVideoTask.java b/src/com/android/retaildemo/DownloadVideoTask.java
new file mode 100644
index 0000000..6da0f3e
--- /dev/null
+++ b/src/com/android/retaildemo/DownloadVideoTask.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.retaildemo;
+
+import android.app.AlertDialog;
+import android.app.DownloadManager;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * Downloads the video from the specified url. If the video is previously downloaded, then uses
+ * that but checks if there is a more recent version of the video available.
+ */
+class DownloadVideoTask {
+    private static final String TAG = "DownloadVideoTask";
+    private static final boolean DEBUG = false;
+
+    private static final int MSG_CHECK_FOR_UPDATE = 1;
+    private static final int MSG_DOWNLOAD_COMPLETE = 2;
+    private static final int MSG_CLEANUP_DOWNLOAD_DIR = 3;
+
+    private static final int CLEANUP_DELAY_MILLIS = 2 * 1000; // 2 seconds
+
+    private Context mContext;
+    private DownloadManager mDlm;
+    private File mDownloadFile;
+    private ResultListener mListener;
+
+    private Handler mHandler;
+
+    private ProgressDialog mProgressDialog;
+    private AlertDialog mErrorMsgDialog;
+    private NetworkChangeReceiver mNetworkChangeReceiver;
+    private String mDownloadUrl;
+    private long mVideoDownloadId;
+    private long mVideoUpdateDownloadId;
+    private String mDownloadedPath;
+
+    public DownloadVideoTask(Context context, String downloadPath, ResultListener listener) {
+        mContext = context;
+        mDownloadFile = new File(downloadPath);
+        mListener = listener;
+
+        mDlm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mDownloadUrl = mContext.getString(R.string.retail_demo_video_download_url);
+    }
+
+    public void run() {
+        mContext.registerReceiver(mDownloadReceiver,
+                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+        // Initialize handler
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mHandler = new ThreadHandler(thread.getLooper());
+
+        // If file already exists, no need to download it again.
+        if (mDownloadFile.exists()) {
+            if (DEBUG) Log.d(TAG, "Using the alreaded downloaded video at "
+                    + mDownloadFile.getPath());
+            mListener.onFileDownloaded(mDownloadFile.getPath());
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_FOR_UPDATE));
+        } else {
+            if (!isConnectedToNetwork()) {
+                mErrorMsgDialog = createErrorMsgDialog(R.string.no_network_connectivity);
+                mErrorMsgDialog.show();
+                mNetworkChangeReceiver = new NetworkChangeReceiver();
+                mContext.registerReceiver(mNetworkChangeReceiver,
+                        new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+                return;
+            }
+            startDownload();
+        }
+    }
+
+    private void startDownload() {
+        final DownloadManager.Request request = createDownloadRequest();
+        mVideoDownloadId = mDlm.enqueue(request);
+        if (DEBUG) Log.d(TAG, "Started downloading the video at " + mDownloadFile.getPath());
+        showProgressDialog();
+    }
+
+    private DownloadManager.Request createDownloadRequest() {
+        final DownloadManager.Request request = new DownloadManager.Request(
+                Uri.parse(mDownloadUrl));
+        request.setDestinationUri(Uri.fromFile(mDownloadFile));
+        return request;
+    }
+
+    private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
+                return;
+            }
+
+            final long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
+            if (id == mVideoDownloadId) {
+                if (checkDownloadsAndSetVideo(id)) {
+                    mProgressDialog.dismiss();
+                }
+            } else if (id == mVideoUpdateDownloadId) {
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_DOWNLOAD_COMPLETE));
+            }
+        }
+    };
+
+    private final class ThreadHandler extends Handler {
+        public ThreadHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CHECK_FOR_UPDATE:
+                    if (!isConnectedToNetwork()) {
+                        mNetworkChangeReceiver = new NetworkChangeReceiver();
+                        mContext.registerReceiver(mNetworkChangeReceiver,
+                                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+                        return;
+                    }
+                    HttpURLConnection conn = null;
+                    try {
+                        conn = (HttpURLConnection) new URL(mDownloadUrl).openConnection();
+                        final long lastModified = mDownloadFile.lastModified();
+                        conn.setIfModifiedSince(lastModified);
+                        conn.connect();
+                        if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
+                            return;
+                        }
+                        final DownloadManager.Request request = createDownloadRequest();
+                        mVideoUpdateDownloadId = mDlm.enqueue(request);
+                        if (DEBUG) Log.d(TAG, "Started downloading the updated video");
+                    } catch (IOException e) {
+                        Log.e(TAG, "Error while checking for an updated video", e);
+                    } finally {
+                        if (conn != null) {
+                            conn.disconnect();
+                        }
+                    }
+                    break;
+                case MSG_DOWNLOAD_COMPLETE:
+                    checkDownloadsAndSetVideo(mVideoUpdateDownloadId);
+                    break;
+                case MSG_CLEANUP_DOWNLOAD_DIR:
+                    // If the video was downloaded to the same location as we needed, then
+                    // nothing else to do.
+                    if (mDownloadFile.getPath().equals(mDownloadedPath)) {
+                        return;
+                    }
+                    if (mDownloadFile.exists()) {
+                        mDownloadFile.delete();
+                    }
+                    if (new File(mDownloadedPath).renameTo(mDownloadFile)) {
+                        mListener.onFileDownloaded(mDownloadFile.getPath());
+                        final String downloadFileName = mDownloadFile.getName();
+                        // Delete other files in the directory
+                        for (File file : mDownloadFile.getParentFile().listFiles()) {
+                            if (file.getName().startsWith(downloadFileName)
+                                    && !file.getPath().equals(mDownloadFile.getPath())) {
+                                file.delete();
+                            }
+                        }
+                    }
+                    break;
+            }
+        }
+    }
+
+    private boolean checkDownloadsAndSetVideo(long downloadId) {
+        final DownloadManager.Query query =
+                new DownloadManager.Query().setFilterById(downloadId);
+        Cursor cursor = mDlm.query(query);
+        try {
+            if (cursor != null & cursor.moveToFirst()) {
+                final int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+                if (cursor.getInt(columnIndex) == DownloadManager.STATUS_SUCCESSFUL) {
+                    mContext.unregisterReceiver(mDownloadReceiver);
+                    if (mNetworkChangeReceiver != null) {
+                        mContext.unregisterReceiver(mNetworkChangeReceiver);
+                    }
+                    final String fileUri = cursor.getString(
+                            cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
+                    mDownloadedPath = Uri.parse(fileUri).getPath();
+                    if (DEBUG) Log.d(TAG, "Video successfully downloaded at " + mDownloadedPath);
+                    mListener.onFileDownloaded(mDownloadedPath);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEANUP_DOWNLOAD_DIR),
+                            CLEANUP_DELAY_MILLIS);
+                    return true;
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return false;
+    }
+
+    private class NetworkChangeReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())
+                    && isConnectedToNetwork()) {
+                if (mDownloadFile.exists()) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_FOR_UPDATE));
+                } else {
+                    mErrorMsgDialog.dismiss();
+                    startDownload();
+                }
+            }
+        }
+    };
+
+    private void showProgressDialog() {
+        mProgressDialog = new ProgressDialog(mContext);
+        mProgressDialog.setMessage(mContext.getString(R.string.downloading_video_msg));
+        mProgressDialog.setIndeterminate(false);
+        mProgressDialog.setCancelable(false);
+        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+        mProgressDialog.show();
+    }
+
+    private boolean isConnectedToNetwork() {
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        NetworkInfo info = cm.getActiveNetworkInfo();
+        return info != null && info.isConnected();
+    }
+
+    private AlertDialog createErrorMsgDialog(int msgResId) {
+        return new AlertDialog.Builder(mContext)
+                .setMessage(msgResId)
+                .setCancelable(false)
+                .create();
+    }
+
+    interface ResultListener {
+        void onFileDownloaded(String downloadedFilePath);
+    }
+}
\ No newline at end of file