/** * */ package ma.car.tishadow.bundle.update.tasks; import java.io.File; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import ma.car.tishadow.bundle.update.BundleUpdateState; import ma.car.tishadow.bundle.update.RequestProxy; import org.appcelerator.kroll.common.Log; import android.app.DownloadManager; import android.app.DownloadManager.Query; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; /** * Represents a task to download the specific bundle. * @author wei.ding */ public class DownloadBundleTask implements Task { private static final String TAG = "DownloadBundleTask"; private final Lock lock = new ReentrantLock(); private final Condition waitForDownloadComplete = lock.newCondition(); /* * (non-Javadoc) * @see ma.car.tishadow.bundle.update.tasks.Task#execute(ma.car.tishadow.bundle.update.tasks.TaskContext) */ @Override public boolean execute(RequestProxy context) { Log.v(TAG, "Starting DownloadBundleTask..."); if (!BundleUpdateManager.isBundleDownloadRequired(context)) { Log.i(TAG, "Don't need to download bundle, because already up-to-date."); return false; } lock.lock(); try { Log.d(TAG, "starting to downloading bundle..."); sendBundleDownloadRequest(context); waitForDownloadComplete.await(); context.markedBundleUpdateStateTo(BundleUpdateState.DOWNLOADED); Log.d(TAG, "DownloadBundleTask done."); return true; } catch (InterruptedException e) { context.markedBundleUpdateStateTo(BundleUpdateState.INTERRUPTED); Log.e(TAG, "Failed to handle post tasks if the bundle would be download later because of bundle update thread is interrupted.", e); } finally { lock.unlock(); } return false; } private void sendBundleDownloadRequest(RequestProxy context) { // Register ACTION_DOWNLOAD_COMPLETE event, and send download request to Android DownloadManager service. Context applicationContext = context.getApplicationContext(); applicationContext.registerReceiver(newDownloadCompleteHandler(context), new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); DownloadManager downloadManager = (DownloadManager) applicationContext.getSystemService(Context.DOWNLOAD_SERVICE); String bundleDownloadUrl = (String) context.getRequestProperty(RequestProxy.Key.BUNDLE_DOWNLOAD_URL); String bundleDecompressDirectory = (String) context.getRequestProperty(RequestProxy.Key.BUNDLE_DECOMPRESS_DIRECTORY); String bundleLocalFileName = bundleDecompressDirectory + ".zip"; File destination = new File(context.getExternalApplicationTemporaryDirectory(), bundleLocalFileName); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(bundleDownloadUrl)); request.setShowRunningNotification(false).setVisibleInDownloadsUi(false); request.setDestinationUri(Uri.fromFile(destination)); long downloadId = downloadManager.enqueue(request); context.getRequestProperties().put(RequestProxy.Key.DOWNLOADING_BUNDLE_REFID, new Long(downloadId)); context.getRequestProperties().put(RequestProxy.Key.DOWNLOAD_DESTINATION_FILENAME, bundleLocalFileName); context.markedBundleUpdateStateTo(BundleUpdateState.DOWNLOADING); Log.i(TAG, "Downloading bundle '" + context.getLatestBundleVerion() + "' from '" + bundleDownloadUrl + "' into directory '" + destination + "'... "); } /** * Create a new download complete handler, which is an implementation of broadcastReceiver to receive notification event from android system. * @param context * @return */ private BroadcastReceiver newDownloadCompleteHandler(final RequestProxy context) { return new BroadcastReceiver() { // TODO The android default DownloadManager may not be stable because of an open bug <a // href='https://code.google.com/p/android/issues/detail?id=18462' target='_blank'>issue 18462</a>, I may need to rewrite this component later. @Override public void onReceive(Context cxt, Intent intent) { Log.i(TAG, "Receiving download complete event intent '" + intent + "'..."); lock.lock(); try { long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L); long storedReferenceId = (Long) context.getRequestProperties().get(RequestProxy.Key.DOWNLOADING_BUNDLE_REFID); if (referenceId != -1 && referenceId == storedReferenceId) { onBundleDownloadCompleted(context, intent); context.getRequestProperties().remove(RequestProxy.Key.DOWNLOADING_BUNDLE_REFID); cxt.unregisterReceiver(this); waitForDownloadComplete.signal(); } } finally { lock.unlock(); } } }; } private void onBundleDownloadCompleted(RequestProxy context, Intent intent) { long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); Query query = new Query(); query.setFilterById(referenceId); Context applicationContext = context.getApplicationContext(); DownloadManager downloadManager = (DownloadManager) applicationContext.getSystemService(Context.DOWNLOAD_SERVICE); Cursor cursor = downloadManager.query(query); if (cursor.moveToFirst()) { int statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); int status = cursor.getInt(statusIndex); int reasonIndex = cursor.getColumnIndex(DownloadManager.COLUMN_REASON); int reason = reasonIndex == -1 ? -1 : cursor.getInt(reasonIndex); int localUriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI); String localUri = cursor.getString(localUriIndex); Log.i(TAG, "Bundle download completed, status(" + status + "), reason(" + reason + "), local uri(" + localUri + ")."); } cursor.close(); } }