diff --git a/VideoLocker/res/layout/activity_course_base.xml b/VideoLocker/res/layout/activity_course_base.xml index 3781a6c11d..bdc3b8743d 100755 --- a/VideoLocker/res/layout/activity_course_base.xml +++ b/VideoLocker/res/layout/activity_course_base.xml @@ -1,12 +1,10 @@ - + android:splitMotionEvents="false"> - - - - - - - - - - - - - - - - callback); - - + + /** * Returns all dmid's which are currently being downloaded for logged in user * This method is used for showing progress in the DownloadModule - * @return + * + * @return */ public List getAllDownloadingVideosDmidList(DataCallback> callback); - - + + /** - * Mark all videos as course deactivated for logged in user + * Mark all videos as course deactivated for logged in user */ public Integer updateAllVideosAsDeactivated(DataCallback callback); - + /** * Mark all videos with enrollment id as course activated for logged in user + * * @param enrollmentId * @return The number of rows affected */ - public Integer updateVideosActivatedForCourse(String enrollmentId, DataCallback callback); - + public Integer updateVideosActivatedForCourse(String enrollmentId, DataCallback + callback); + /** * Returns all Deactivated videos for logged in user + * * @param callback */ public List getAllDeactivatedVideos(DataCallback> callback); - - + + /** - * Mark the Video as online and reset the filepath and dmid for logged in user - * @param videoId - IVideoModel object + * Mark the Video as online and reset the filepath and dmid for logged in user + * + * @param videoId - IVideoModel object */ public Integer updateVideoAsOnlineByVideoId(String videoId, DataCallback callback); - - + + /** * Returns count of Videos with passed DMID for logged in user + * * @param callback */ public Integer getVideoCountBydmId(long dmId, DataCallback callback); - - + + /** - * Returns true if Video is downloaded in Chapter for logged in user + * Returns true if Video is downloaded in Chapter for logged in user + * * @param enrollmentId - course which has the chapter * @param chapter */ - public Boolean isVideoDownloadedInChapter(String enrollmentId, - String chapter, DataCallback callback); - + public Boolean isVideoDownloadedInChapter(String enrollmentId, + String chapter, DataCallback callback); + /** - * Returns number of videos marked as downloading/downloaded in Chapter for logged in user + * Returns number of videos marked as downloading/downloaded in Chapter for logged in user + * * @param enrollmentId - course which has the chapter * @param chapter * @return - Number of videos not online */ - public Integer getVideosCountByChapter(String enrollmentId, - String chapter, DataCallback callback); + public Integer getVideosCountByChapter(String enrollmentId, + String chapter, DataCallback callback); /** * Return number of videos marked as web_view_only inChapter for logged in user + * * @param enrollmentId * @param chapter * @param callback @@ -100,284 +109,369 @@ public Integer getWebOnlyVideosCountByChapter(String enrollmentId, String chapte /** * Returns true if any video downloading is in progress for chapter + * * @param enrollmentId - course which has the chapter * @param chapter * @return - Number of Downloaded videos */ - public Boolean isVideoDownloadingInChapter(String enrollmentId, - String chapter, DataCallback callback); - - + public Boolean isVideoDownloadingInChapter(String enrollmentId, + String chapter, DataCallback callback); + + /** - * Returns dmId's of all downloading videos for given Chapter of logged in user + * Returns dmId's of all downloading videos for given Chapter of logged in user + * * @param enrollmentId - course which has the chapter * @param chapter */ - public List getDownloadingVideoDmIdsForChapter(String enrollmentId, String chapter, - DataCallback> callback); - - + public List getDownloadingVideoDmIdsForChapter(String enrollmentId, String chapter, + DataCallback> callback); + + /** * Returns true if any video downloading is in progress for Section + * * @param enrollmentId - course which has the chapter * @param chapter * @param section */ public Boolean isVideoDownloadingInSection(String enrollmentId, String chapter, - String section, DataCallback callback); - - + String section, DataCallback callback); + /** - * Returns dmId's of all downloading videos for given section of logged in user - * @param enrollmentId - course which has the chapter - * @param chapter - * @param section - */ - public List getDownloadingVideoDmIdsForSection(String enrollmentId, String chapter, - String section, final DataCallback> callback); - - + * Returns {@link android.app.DownloadManager} IDs of all downloading videos in a given section. + * + * @param enrollmentId course which has the chapter + * @param chapter the chapter + * @param section the section inside chapter + * @param callback callback to return results to + * @return If the callback is null, returns an array containing the IDs for the downloading + * videos, or an empty array if there are no videos downloading in the section. Otherwise, + * returns null. + */ + public long[] getDownloadingVideoDmIdsForSection(String enrollmentId, String chapter, + String section, final + DataCallback> callback); + /** - * Returns number of videos marked as downloading/downloaded in Section for logged in user + * Returns the count of downloading videos for given section + * + * @param enrollmentId course which has the chapter + * @param chapter the chapter + * @param section the section inside chapter + * @param callback callback to return results to + * @return Count of downloading videos for a given section + */ + public int getDownloadingVideosCountForSection(String enrollmentId, String chapter, + String section, final DataCallback + callback); + + /** + * Returns number of videos marked as downloading/downloaded in Section for logged in user + * * @param enrollmentId - course which has the chapter * @param chapter * @param section */ - public Integer getVideosCountBySection(String enrollmentId, String chapter, - String section, DataCallback callback); + public Integer getVideosCountBySection(String enrollmentId, String chapter, + String section, DataCallback callback); /** * Returns true if Video is downloaded in Section for logged in user */ public Boolean isVideoDownloadedInSection(String enrollmentId, - String chapter, String section, DataCallback callback); + String chapter, String section, + DataCallback callback); + + /** + * Returns dmId's of all downloaded videos for given section of logged in user + * + * @param enrollmentId course which has the chapter + * @param chapter the chapter + * @param section the section inside chapter + * @param callback callback to return results to + * @return If the callback is null, returns an array containing the IDs for the downloaded + * videos, or an empty array if there are no videos downloaded in the section. Otherwise, + * returns null. + */ + public long[] getDownloadedVideoDmIdsForSection(String enrollmentId, String chapter, + String section, final + DataCallback> callback); + + /** + * Returns the count of downloaded videos for given section + * + * @param enrollmentId course which has the chapter + * @param chapter the chapter + * @param section the section inside chapter + * @param callback callback to return results to + * @return Count of downloaded videos for a given section + */ + public int getDownloadedVideosCountForSection(String enrollmentId, String chapter, + String section, final DataCallback + callback); + /** * get number of videos marked as webOnly + * * @param enrollmentId * @param chapter * @param section * @param callback * @return */ - public Integer getWebOnlyVideosCountBySection(String enrollmentId, String chapter, String section, + public Integer getWebOnlyVideosCountBySection(String enrollmentId, String chapter, String + section, final DataCallback callback); /** - * Update a Video's watched state - * @param videoId - Id of video for which status needs to change - * @param state - Status flag to be set for changing Video watched state + * Update a Video's watched state + * + * @param videoId - Id of video for which status needs to change + * @param state - Status flag to be set for changing Video watched state */ public Integer updateVideoWatchedState(String videoId, WatchedState state, - DataCallback callback); - - + DataCallback callback); + + /** - * Update a Video's last watched time - * @param videoId - Id of video for which status needs to change - * @param offset - Last Played offset + * Update a Video's last watched time + * + * @param videoId - Id of video for which status needs to change + * @param offset - Last Played offset */ public Integer updateVideoLastPlayedOffset(String videoId, int offset, - DataCallback callback); - - + DataCallback callback); + + /** - * Insert Download Entry in the database - * @param de - IVideoModel object + * Insert Download Entry in the database + * + * @param de - IVideoModel object * @param callback * @return - the row ID of the newly inserted row, or -1 if an error occurred */ public Long addVideoData(VideoModel de, DataCallback callback); - + /** * Returns VideoEntry for the passed VideoId + * * @param videoId * @param callback */ public VideoModel getVideoEntryByVideoId(String videoId, DataCallback callback); - + /** - * Returns {@link org.edx.mobile.model.VideoModel} which is downloaded or download is in progress for given videoUrl. + * Returns {@link org.edx.mobile.model.VideoModel} which is downloaded or download is in + * progress for given videoUrl. + * * @param videoUrl * @param callback * @return */ VideoModel getVideoByVideoUrl(String videoUrl, DataCallback callback); - - + + /** - * Marks given Video as online and sets dmid to -1 so that this video is identified as NOT_DOWNLAODED. - * File path for this video is made empty so as to avoid access to non-existing file. + * Marks given Video as online and sets dmid to -1 so that this video is identified as + * NOT_DOWNLAODED. + * File path for this video is made empty so as to avoid access to non-existing file. * NOTE: If there are multiple videos with same URL that are marked as Downloaded, * Only the reference should be removed and not the downloaded file - * @param de - IVideoModel object + * + * @param de - IVideoModel object * @param callback * @return - the row ID of the newly inserted row, or -1 if an error occurred */ Integer deleteVideoByVideoId(VideoModel de, DataCallback callback); - - + + /** * Returns if a IVideoModel with the same video URL is downloaded * This method is used to display the progress if video is already downloaded + * * @param url */ public Boolean isVideoFilePresentByUrl(String url, DataCallback callback); - - + + /** * This method updates info for Videos with the same URL and have been enqueued for downloading + * * @param model * @param callback */ public Integer updateDownloadingVideoInfoByVideoId(VideoModel model, - DataCallback callback); - + DataCallback callback); + /** * This method marks the Video as Downloading when enqueued for Download + * * @param model * @param callback */ public Integer updateAsDownloadingByVideoId(VideoModel model, - DataCallback callback); - + DataCallback callback); + /** * Returns list of All VideoEntries which are currently being downloaded + * * @return */ public List getListOfOngoingDownloads(DataCallback> callback); - - + + /** - * Returns no of Videos which have been completely + * Returns no of Videos which have been completely * downloaded and marked as Downloaded in the DB + * * @return - count of Videos downloaded */ public Integer getVideosDownloadedCount(DataCallback callback); - + /** - * Returns Count of number of Downloaded Videos in the Course by Course ID + * Returns Count of number of Downloaded Videos in the Course by Course ID + * * @param courseId * @param callback */ public Integer getDownloadedVideoCountByCourse(String courseId, DataCallback callback); - - + + /** - * Returns Downloaded Videos in the Course by Course ID + * Returns Downloaded Videos in the Course by Course ID + * * @param courseId * @param callback */ public List getDownloadedVideoListForCourse(String courseId, - DataCallback> callback); - - + DataCallback> + callback); + + /** - * Returns Size in bytes of Downloaded Videos in the Course by Course ID + * Returns Size in bytes of Downloaded Videos in the Course by Course ID + * * @param courseId * @param callback */ public Long getDownloadedVideosSizeByCourse(String courseId, DataCallback callback); - + /** - * Returns IVideoModel object if entry exists with Video status set as + * Returns IVideoModel object if entry exists with Video status set as * downloaded with the given URL */ public VideoModel getIVideoModelByVideoUrl(String videoUrl, - DataCallback callback); - - + DataCallback callback); + + /** - * Return true if IVideoModel for associated dmId is present in db. Do not use username + * Return true if IVideoModel for associated dmId is present in db. Do not use username * as this function is to check if the dmid is of VideoLocker or other application + * * @param dmId * @return */ public Boolean isDmIdExists(long dmId, DataCallback callback); - + /** - * Marks the download as complete for the given dmid. - * NOTE - This should be done irrespective of username as if the user is + * Marks the download as complete for the given dmid. + * NOTE - This should be done irrespective of username as if the user is * logged out and download is in progress, it should update download complete in the db. + * * @param dmId * @return */ - public Integer updateDownloadCompleteInfoByDmId(long dmId, - VideoModel de, DataCallback callback); + public Integer updateDownloadCompleteInfoByDmId(long dmId, + VideoModel de, DataCallback callback); /** * Returns list of all videos from the database. + * * @return */ List getAllVideos(String username, DataCallback> DataCallback); /** * Removes all records of given username from the datbase. + * * @param username */ void clearDataByUser(String username); /** * This method gives the WatchedState stored in the DB for VideoID + * * @param videoId * @param dataCallback */ WatchedState getWatchedStateForVideoId(String videoId, - DataCallback dataCallback); + DataCallback dataCallback); /** * Returns count of videos which have given URL as their video URL. + * * @param videoUrl * @param callback * @return */ Integer getVideoCountByVideoUrl(String videoUrl, DataCallback callback); - + /** * Returns count of videos which have given URL as their video URL. + * * @param dmId * @param callback * @return */ VideoModel getDownloadEntryByDmId(long dmId, DataCallback callback); - + /** * This function is used to getting all sorted Downloads based on Download date */ public List getSortedDownloadsByDownloadedDateForCourseId(String courseId, - DataCallback> callback); - + DataCallback> callback); + /** * This method gives the WatchedState stored in the DB for VideoID + * * @param videoId * @param dataCallback */ DownloadedState getDownloadedStateForVideoId(String videoId, - DataCallback dataCallback); + DataCallback dataCallback); /** - * Return true if any Video is marked as Downloading for the courseId in the database for logged in user + * Return true if any Video is marked as Downloading for the courseId in the database for + * logged in user * Used to handle reloading of Section listing + * * @return boolean flag if download is in progress */ public Boolean isAnyVideoDownloadingInCourse(DataCallback callback, String courseId); /** - * Return true if any Video is marked as Downloading for a section in the database for logged in user + * Return true if any Video is marked as Downloading for a section in the database for logged + * in user * Used to handle reloading of subsection listing + * * @return boolean flag if download is in progress */ - public Boolean isAnyVideoDownloadingInSection(DataCallback callback, String courseId, String section); + public Boolean isAnyVideoDownloadingInSection(DataCallback callback, String + courseId, String section); /** - * Return true if any Video is marked as Downloading for a subsection in the database for logged in user + * Return true if any Video is marked as Downloading for a subsection in the database for + * logged in user * Used to handle reloading of Video listing + * * @return boolean flag if download is in progress */ - public Boolean isAnyVideoDownloadingInSubSection(DataCallback callback, String courseId, + public Boolean isAnyVideoDownloadingInSubSection(DataCallback callback, String + courseId, String section, String subSection); /** - * * @param userName */ public void setUserName(String userName); diff --git a/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseBaseImpl.java b/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseBaseImpl.java index 50c0c91b10..bde11f03c8 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseBaseImpl.java +++ b/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseBaseImpl.java @@ -9,17 +9,17 @@ class IDatabaseBaseImpl implements Runnable { + protected static final Logger logger = new Logger(IDatabaseBaseImpl.class.getName()); + protected Context context; private DbHelper helper; private Queue> opQueue = new LinkedList>(); private boolean isQueueProcessing = false; - protected static final Logger logger = new Logger(IDatabaseBaseImpl.class.getName()); - protected Context context; public IDatabaseBaseImpl(Context context) { this.context = context; helper = new DbHelper(context); } - + @Override public void run() { if (isQueueProcessing) { @@ -27,37 +27,38 @@ public void run() { // this will NOT allow multiple threads to process operation queue return; } - + do { // mark queue being processed isQueueProcessing = true; - + IDbOperation op = getNextQueuedOperation(); - + if (op == null) { break; } // perform the datbase operation - execute(op); - } while(true); - + execute(op); + } while (true); + // mark queue not being processed isQueueProcessing = false; - // logger.debug("All database operations completed, queue is empty"); + // logger.debug("All database operations completed, queue is empty"); } - + /** * Executes given database operation. This is a blocking call. * Returns result of the operation. + * * @param op - * @return + * @return */ private synchronized T execute(IDbOperation op) { // perform this database operation synchronized (helper) { T result = (T) op.requestExecute(helper.getDatabase()); - + return result; } } @@ -65,8 +66,9 @@ private synchronized T execute(IDbOperation op) { /** * Enqueues given database operation to the operation queue and starts processing the queue, * if not already started. - * Operation is executed in a queue in background thread if callback is provided for the operation and this method returns null. + * Operation is executed in a queue in background thread if callback is provided for the operation and this method returns null. * Otherwise this is a blocking call and returns result object. + * * @param operation */ public synchronized T enqueue(IDbOperation operation) { @@ -74,20 +76,21 @@ public synchronized T enqueue(IDbOperation operation) { if (operation.getCallback() == null) { return execute(operation); } - + // add non-blocking operations to the queue and process in sequence synchronized (opQueue) { opQueue.add(operation); } - + // start processing the queue as we have a database operation to be processed new Thread(this).start(); - + return null; } - + /** * Returns and removes the next operation from the operation queue. + * * @return */ private IDbOperation getNextQueuedOperation() { @@ -95,7 +98,7 @@ private IDbOperation getNextQueuedOperation() { if (opQueue.isEmpty()) { return null; } - + return opQueue.remove(); } } diff --git a/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java b/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java index 193e40a199..0b4682e630 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java +++ b/VideoLocker/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java @@ -6,6 +6,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; +import org.apache.commons.lang.ArrayUtils; import org.edx.mobile.model.VideoModel; import org.edx.mobile.model.api.ProfileModel; import org.edx.mobile.model.db.DownloadEntry.DownloadedState; @@ -23,13 +24,13 @@ public class IDatabaseImpl extends IDatabaseBaseImpl implements IDatabase { private String username; @Inject - public IDatabaseImpl( Context context ) { + public IDatabaseImpl(Context context) { super(context); } - private String username(){ - if ( username == null ) { + private String username() { + if (username == null) { UserPrefs userprefs = new UserPrefs(context); if (userprefs != null) { ProfileModel profile = userprefs.getProfile(); @@ -43,18 +44,21 @@ private String username(){ @Override public Boolean isAnyVideoDownloading(final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.USERNAME + "=? AND "+ DbStructure.Column.DOWNLOADED + "=?", - new String[] { username(), String.valueOf(DownloadedState.DOWNLOADING.ordinal()) }, null); + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.USERNAME + "=? AND " + DbStructure.Column.DOWNLOADED + "=?", + new String[]{username(), String.valueOf(DownloadedState.DOWNLOADING.ordinal())}, + null); op.setCallback(callback); return enqueue(op); } @Override - public List getAllDownloadingVideosDmidList(final DataCallback> callback){ - DbOperationGetColumn op = new DbOperationGetColumn(true,DbStructure.Table.DOWNLOADS, new String[]{DbStructure.Column.DM_ID}, + public List getAllDownloadingVideosDmidList(final DataCallback> callback) { + DbOperationGetColumn op = new DbOperationGetColumn(true, DbStructure.Table + .DOWNLOADS, new String[]{DbStructure.Column.DM_ID}, DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, null, Long.class); + new String[]{String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, + null, Long.class); op.setCallback(callback); return enqueue(op); } @@ -65,20 +69,21 @@ public Integer updateAllVideosAsDeactivated(final DataCallback callback values.put(DbStructure.Column.IS_COURSE_ACTIVE, false); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.USERNAME + "=?", new String[] {username()}); + DbStructure.Column.USERNAME + "=?", new String[]{username()}); op.setCallback(callback); return enqueue(op); } @Override - public Integer updateVideosActivatedForCourse(String enrollmentId, final DataCallback callback) { + public Integer updateVideosActivatedForCourse(String enrollmentId, final + DataCallback callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.IS_COURSE_ACTIVE, true); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.EID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {enrollmentId, username()}); + DbStructure.Column.EID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{enrollmentId, username()}); op.setCallback(callback); return enqueue(op); } @@ -86,23 +91,25 @@ public Integer updateVideosActivatedForCourse(String enrollmentId, final DataCal @Override public List getAllDeactivatedVideos(final DataCallback> callback) { - DbOperationGetVideos op = new DbOperationGetVideos(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.IS_COURSE_ACTIVE + "=? AND "+ DbStructure.Column.USERNAME + "=? " , - new String[] { "0", username()}, null); + DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.IS_COURSE_ACTIVE + "=? AND " + DbStructure.Column.USERNAME + + "=? ", + new String[]{"0", username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public Integer updateVideoAsOnlineByVideoId(String videoId, final DataCallback callback) { + public Integer updateVideoAsOnlineByVideoId(String videoId, final DataCallback + callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.DM_ID, 0); values.put(DbStructure.Column.FILEPATH, ""); values.put(DbStructure.Column.DOWNLOADED, DownloadedState.ONLINE.ordinal()); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {videoId, username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}); op.setCallback(callback); return enqueue(op); } @@ -110,157 +117,236 @@ public Integer updateVideoAsOnlineByVideoId(String videoId, final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.DM_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { String.valueOf(dmId), username() }, null); + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.DM_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(dmId), username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Boolean isVideoDownloadedInChapter(String enrollmentId, - String chapter, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { chapter, enrollmentId, - String.valueOf(DownloadedState.DOWNLOADED.ordinal()),username()}, null); + String chapter, final DataCallback + callback) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + + "=?", + new String[]{chapter, enrollmentId, + String.valueOf(DownloadedState.DOWNLOADED.ordinal()), username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public Integer getVideosCountByChapter(String enrollmentId, String chapter, - final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.DOWNLOADED + "!=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { chapter, enrollmentId, - String.valueOf(DownloadedState.ONLINE.ordinal()),username()}, null); + public Integer getVideosCountByChapter(String enrollmentId, String chapter, + final DataCallback callback) { + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.DOWNLOADED + "!=? AND " + DbStructure.Column + .USERNAME + "=?", + new String[]{chapter, enrollmentId, + String.valueOf(DownloadedState.ONLINE.ordinal()), username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Integer getWebOnlyVideosCountByChapter(String enrollmentId, String chapter, - final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, + final DataCallback callback) { + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.VIDEO_FOR_WEB_ONLY + "==1 AND " + DbStructure.Column.USERNAME + "=?", - new String[] { chapter, enrollmentId, username()}, null); + + DbStructure.Column.VIDEO_FOR_WEB_ONLY + "==1 AND " + DbStructure.Column + .USERNAME + "=?", + new String[]{chapter, enrollmentId, username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Boolean isVideoDownloadingInChapter(String enrollmentId, - String chapter, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { chapter, enrollmentId, - String.valueOf(DownloadedState.DOWNLOADING.ordinal()),username()}, null); + String chapter, final DataCallback + callback) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + + "=?", + new String[]{chapter, enrollmentId, + String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, null); op.setCallback(callback); return enqueue(op); } @Override public List getDownloadingVideoDmIdsForChapter(String enrollmentId, - String chapter, final DataCallback> callback) { - DbOperationGetColumn op = new DbOperationGetColumn(true,DbStructure.Table.DOWNLOADS, + String chapter, final + DataCallback> callback) { + DbOperationGetColumn op = new DbOperationGetColumn(true, DbStructure.Table + .DOWNLOADS, new String[]{DbStructure.Column.DM_ID}, - DbStructure.Column.DOWNLOADED + "=? AND "+ DbStructure.Column.EID + "=? AND " - + DbStructure.Column.CHAPTER + "=? AND "+ DbStructure.Column.USERNAME + "=?", - new String[] {String.valueOf(DownloadedState.DOWNLOADING.ordinal()), - enrollmentId, chapter,username()}, null, Long.class); + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.USERNAME + + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADING.ordinal()), + enrollmentId, chapter, username()}, null, Long.class); op.setCallback(callback); return enqueue(op); } @Override public Boolean isVideoDownloadingInSection(String enrollmentId, - String chapter, String section, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " - + DbStructure.Column.EID + "=? AND "+ DbStructure.Column.DOWNLOADED + "=? AND " + String chapter, String section, final + DataCallback callback) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {section, chapter, enrollmentId, - String.valueOf(DownloadedState.DOWNLOADING.ordinal()),username()}, null); + new String[]{section, chapter, enrollmentId, + String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public List getDownloadingVideoDmIdsForSection(String enrollmentId, String chapter, - String section , final DataCallback> callback ) { - DbOperationGetColumn op = new DbOperationGetColumn(true,DbStructure.Table.DOWNLOADS, + public long[] getDownloadingVideoDmIdsForSection(String enrollmentId, String chapter, + String section, final + DataCallback> callback) { + DbOperationGetColumn op = new DbOperationGetColumn(true, DbStructure.Table + .DOWNLOADS, new String[]{DbStructure.Column.DM_ID}, - DbStructure.Column.DOWNLOADED + "=? AND "+ DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.EID + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {String.valueOf(DownloadedState.DOWNLOADING.ordinal()), - enrollmentId, chapter, section, username()}, null, Long.class); + new String[]{String.valueOf(DownloadedState.DOWNLOADING.ordinal()), + enrollmentId, chapter, section, username()}, null, Long.class); op.setCallback(callback); - return enqueue(op); + List downloadingList = enqueue(op); + if (callback != null) { + return null; + } else { + return ArrayUtils.toPrimitive(downloadingList.toArray(new Long[downloadingList.size()])); + } } + @Override + public int getDownloadingVideosCountForSection(String enrollmentId, String chapter, + String section, final DataCallback + callback) { + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.DM_ID}, + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.SECTION + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADING.ordinal()), + enrollmentId, chapter, section, username()}, null); + op.setCallback(callback); + return enqueue(op); + } @Override public Boolean isVideoDownloadedInSection(String enrollmentId, - String chapter, String section, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { section, chapter, enrollmentId, - String.valueOf(DownloadedState.DOWNLOADED.ordinal()),username()}, null); + String chapter, String section, final + DataCallback callback) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + + "=?", + new String[]{section, chapter, enrollmentId, + String.valueOf(DownloadedState.DOWNLOADED.ordinal()), username()}, null); + op.setCallback(callback); + return enqueue(op); + } + + @Override + public long[] getDownloadedVideoDmIdsForSection(String enrollmentId, String chapter, + String section, final + DataCallback> callback) { + DbOperationGetColumn op = new DbOperationGetColumn(true, DbStructure.Table + .DOWNLOADS, + new String[]{DbStructure.Column.DM_ID}, + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.SECTION + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADED.ordinal()), + enrollmentId, chapter, section, username()}, null, Long.class); + op.setCallback(callback); + List downloadedList = enqueue(op); + if (callback != null) { + return null; + } else { + return ArrayUtils.toPrimitive(downloadedList.toArray(new Long[downloadedList.size()])); + } + } + + @Override + public int getDownloadedVideosCountForSection(String enrollmentId, String chapter, + String section, final DataCallback + callback) { + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.DM_ID}, + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.SECTION + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADED.ordinal()), + enrollmentId, chapter, section, username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Integer getVideosCountBySection(String enrollmentId, String chapter, - String section, final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.SECTION + "=? AND "+DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.DOWNLOADED + "!=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { section, chapter, enrollmentId, - String.valueOf(DownloadedState.ONLINE.ordinal()),username()}, null); + String section, final DataCallback callback) { + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.DOWNLOADED + "!=? AND " + DbStructure.Column + .USERNAME + "=?", + new String[]{section, chapter, enrollmentId, + String.valueOf(DownloadedState.ONLINE.ordinal()), username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public Integer getWebOnlyVideosCountBySection(String enrollmentId, String chapter, String section, + public Integer getWebOnlyVideosCountBySection(String enrollmentId, String chapter, String + section, final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.SECTION + "=? AND "+DbStructure.Column.CHAPTER + "=? AND " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.VIDEO_FOR_WEB_ONLY + "==1 AND " + DbStructure.Column.USERNAME + "=?", - new String[] { section, chapter, enrollmentId, username()}, null); + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.EID + "=? AND " + + DbStructure.Column.VIDEO_FOR_WEB_ONLY + "==1 AND " + DbStructure.Column + .USERNAME + "=?", + new String[]{section, chapter, enrollmentId, username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public Integer updateVideoWatchedState(String videoId, WatchedState status, - final DataCallback callback) { + public Integer updateVideoWatchedState(String videoId, WatchedState status, + final DataCallback callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.WATCHED, status.ordinal()); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {videoId, username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}); op.setCallback(callback); return enqueue(op); } @@ -268,13 +354,13 @@ public Integer updateVideoWatchedState(String videoId, WatchedState status, @Override public Integer updateVideoLastPlayedOffset(String videoId, int offset, - final DataCallback callback) { + final DataCallback callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.LAST_PLAYED_OFFSET, offset); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {videoId, username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}); op.setCallback(callback); return enqueue(op); } @@ -310,7 +396,7 @@ public Long addVideoData(final VideoModel de, final DataCallback callback) op.setCallback(callback); return enqueue(op); } else { - if(callback!=null){ + if (callback != null) { callback.sendResult(0L); } logger.warn("Not inserting, this seems a duplicate record"); @@ -322,24 +408,27 @@ public Long addVideoData(final VideoModel de, final DataCallback callback) /** * Returns download entry for given video id. + * * @param videoId * @return */ - public VideoModel getVideoEntryByVideoId(String videoId, final DataCallback callback) { - DbOperationGetVideo op = new DbOperationGetVideo(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.VIDEO_ID + "=? AND "+ DbStructure.Column.USERNAME + "=?" , - new String[] { videoId, username()}, null); + public VideoModel getVideoEntryByVideoId(String videoId, final DataCallback + callback) { + DbOperationGetVideo op = new DbOperationGetVideo(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}, null); op.setCallback(callback); return enqueue(op); } @Override public VideoModel getVideoByVideoUrl(String videoUrl, - DataCallback callback) { - DbOperationGetVideo op = new DbOperationGetVideo(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.URL + "=? AND "+DbStructure.Column.DOWNLOADED + "!=? AND " - + DbStructure.Column.USERNAME + "=?" , - new String[] { videoUrl, String.valueOf(DownloadedState.ONLINE.ordinal()), username()}, null); + DataCallback callback) { + DbOperationGetVideo op = new DbOperationGetVideo(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.URL + "=? AND " + DbStructure.Column.DOWNLOADED + "!=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{videoUrl, String.valueOf(DownloadedState.ONLINE.ordinal()), username + ()}, null); op.setCallback(callback); return enqueue(op); } @@ -352,8 +441,8 @@ public Integer deleteVideoByVideoId(VideoModel video, DataCallback call values.put(DbStructure.Column.FILEPATH, ""); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { video.getVideoId(), username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{video.getVideoId(), username()}); op.setCallback(callback); return enqueue(op); } @@ -361,18 +450,18 @@ public Integer deleteVideoByVideoId(VideoModel video, DataCallback call @Override public Boolean isVideoFilePresentByUrl(String videoUrl, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.URL + "=? AND "+ DbStructure.Column.DOWNLOADED + "=? AND " - + DbStructure.Column.USERNAME + "=?", - new String[] { videoUrl,String.valueOf(DownloadedState.DOWNLOADED.ordinal()), - username()}, null); + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.URL + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{videoUrl, String.valueOf(DownloadedState.DOWNLOADED.ordinal()), + username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Integer updateDownloadingVideoInfoByVideoId(VideoModel model, - DataCallback callback) { + DataCallback callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.DM_ID, model.getDmId()); values.put(DbStructure.Column.DOWNLOADED, model.getDownloadedStateOrdinal()); @@ -382,8 +471,8 @@ public Integer updateDownloadingVideoInfoByVideoId(VideoModel model, values.put(DbStructure.Column.IS_COURSE_ACTIVE, model.isCourseActive()); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { model.getVideoId(),username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{model.getVideoId(), username()}); op.setCallback(callback); return enqueue(op); } @@ -391,23 +480,25 @@ public Integer updateDownloadingVideoInfoByVideoId(VideoModel model, @Override public Integer updateAsDownloadingByVideoId(VideoModel model, - DataCallback callback) { + DataCallback callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.DM_ID, model.getDmId()); values.put(DbStructure.Column.DOWNLOADED, DownloadedState.DOWNLOADING.ordinal()); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { model.getVideoId(),username()}); + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{model.getVideoId(), username()}); op.setCallback(callback); return enqueue(op); } @Override - public List getListOfOngoingDownloads(final DataCallback> callback) { - DbOperationGetVideos op = new DbOperationGetVideos(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, null); + public List getListOfOngoingDownloads(final DataCallback> + callback) { + DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADING.ordinal()), username()}, + null); op.setCallback(callback); return enqueue(op); } @@ -415,21 +506,25 @@ public List getListOfOngoingDownloads(final DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - null, - DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] {String.valueOf(DownloadedState.DOWNLOADED.ordinal()), username()}, null); + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + null, + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{String.valueOf(DownloadedState.DOWNLOADED.ordinal()), username()}, + null); op.setCallback(callback); return enqueue(op); } @Override - public Integer getDownloadedVideoCountByCourse(String courseId, final DataCallback callback){ - DbOperationGetCount op = new DbOperationGetCount(true,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { courseId, String.valueOf(DownloadedState.DOWNLOADED.ordinal()), username() }, + public Integer getDownloadedVideoCountByCourse(String courseId, final DataCallback + callback) { + DbOperationGetCount op = new DbOperationGetCount(true, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{courseId, String.valueOf(DownloadedState.DOWNLOADED.ordinal()), + username()}, null); op.setCallback(callback); return enqueue(op); @@ -437,26 +532,29 @@ public Integer getDownloadedVideoCountByCourse(String courseId, final DataCallba @Override - public List getDownloadedVideoListForCourse(String courseId, final DataCallback> callback){ - DbOperationGetVideos op = new DbOperationGetVideos(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " - + DbStructure.Column.USERNAME + "=?", - new String[] {courseId, String.valueOf(DownloadedState.DOWNLOADED.ordinal()), - username()}, null); + public List getDownloadedVideoListForCourse(String courseId, final + DataCallback> callback) { + DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{courseId, String.valueOf(DownloadedState.DOWNLOADED.ordinal()), + username()}, null); op.setCallback(callback); return enqueue(op); } @Override - public Long getDownloadedVideosSizeByCourse(String courseId, final DataCallback callback){ + public Long getDownloadedVideosSizeByCourse(String courseId, final DataCallback + callback) { String sqlQuery = "SELECT SUM(" + DbStructure.Column.SIZE + ") FROM " - + DbStructure.Table.DOWNLOADS + " WHERE " + + DbStructure.Table.DOWNLOADS + " WHERE " + DbStructure.Column.EID + "=? AND " - + DbStructure.Column.USERNAME + "=? AND " + + DbStructure.Column.USERNAME + "=? AND " + DbStructure.Column.DOWNLOADED + "=?"; DbOperationSingleValueByRawQuery op = new DbOperationSingleValueByRawQuery( sqlQuery, - new String[] { courseId, username(), String.valueOf(DownloadedState.DOWNLOADED.ordinal()) }, + new String[]{courseId, username(), String.valueOf(DownloadedState.DOWNLOADED + .ordinal())}, Long.class); op.setCallback(callback); return enqueue(op); @@ -465,39 +563,40 @@ public Long getDownloadedVideosSizeByCourse(String courseId, final DataCallback< @Override public List getSortedDownloadsByDownloadedDateForCourseId(String courseId, - DataCallback> callback) { - DbOperationGetVideos op = new DbOperationGetVideos(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " - + DbStructure.Column.USERNAME + "=?", - new String[] { courseId, String.valueOf( - DownloadedState.DOWNLOADED.ordinal()), username() }, - DbStructure.Column.DOWNLOADED_ON + " DESC"); + DataCallback> callback) { + DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.DOWNLOADED + "=? AND " + + DbStructure.Column.USERNAME + "=?", + new String[]{courseId, String.valueOf( + DownloadedState.DOWNLOADED.ordinal()), username()}, + DbStructure.Column.DOWNLOADED_ON + " DESC"); op.setCallback(callback); return enqueue(op); } @Override public VideoModel getIVideoModelByVideoUrl(String videoUrl, - final DataCallback callback) { - DbOperationGetVideo op = new DbOperationGetVideo(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.URL + "=? AND "+ DbStructure.Column.USERNAME + "=?" , - new String[] { videoUrl, username()}, null); + final DataCallback callback) { + DbOperationGetVideo op = new DbOperationGetVideo(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.URL + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoUrl, username()}, null); op.setCallback(callback); return enqueue(op); } @Override public Boolean isDmIdExists(long dmId, final DataCallback callback) { - DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.USERNAME + "=? AND "+ DbStructure.Column.DM_ID + "=?", - new String[] { username(), String.valueOf(dmId) }, null); + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.USERNAME + "=? AND " + DbStructure.Column.DM_ID + "=?", + new String[]{username(), String.valueOf(dmId)}, null); op.setCallback(callback); return enqueue(op); } @Override - public Integer updateDownloadCompleteInfoByDmId(long dmId, - VideoModel model, DataCallback callback) { + public Integer updateDownloadCompleteInfoByDmId(long dmId, + VideoModel model, DataCallback + callback) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.SIZE, model.getSize()); values.put(DbStructure.Column.DURATION, model.getDuration()); @@ -510,26 +609,27 @@ public Integer updateDownloadCompleteInfoByDmId(long dmId, values.put(DbStructure.Column.DOWNLOADED_ON, model.getDownloadedOn()); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.DOWNLOADS, values, - DbStructure.Column.DM_ID + "=? AND " + DbStructure.Column.DOWNLOADED + "!=?", - new String[] { String.valueOf(dmId), - String.valueOf(DownloadedState.ONLINE.ordinal())}); + DbStructure.Column.DM_ID + "=? AND " + DbStructure.Column.DOWNLOADED + "!=?", + new String[]{String.valueOf(dmId), + String.valueOf(DownloadedState.ONLINE.ordinal())}); op.setCallback(callback); return enqueue(op); } @Override - public List getAllVideos(String username, final DataCallback> callback) { - DbOperationGetVideos op = new DbOperationGetVideos(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.USERNAME + "=?", new String[] { username() }, null); + public List getAllVideos(String username, final DataCallback> + callback) { + DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.USERNAME + "=?", new String[]{username()}, null); op.setCallback(callback); return enqueue(op); } @Override public void clearDataByUser(String username) { - DbOperationDelete op = new DbOperationDelete(DbStructure.Table.DOWNLOADS, - DbStructure.Column.USERNAME + "=?", - new String[] { username() } ); + DbOperationDelete op = new DbOperationDelete(DbStructure.Table.DOWNLOADS, + DbStructure.Column.USERNAME + "=?", + new String[]{username()}); enqueue(op); } @@ -541,17 +641,18 @@ public void release() { @Override public WatchedState getWatchedStateForVideoId(String videoId, - final DataCallback dataCallback) { - DbOperationGetColumn op = new DbOperationGetColumn(false,DbStructure.Table.DOWNLOADS, - new String[] { DbStructure.Column.WATCHED }, - DbStructure.Column.VIDEO_ID + "=? AND "+ DbStructure.Column.USERNAME + "=?" , - new String[] { videoId, username()}, null, Integer.class); + final DataCallback dataCallback) { + DbOperationGetColumn op = new DbOperationGetColumn(false, DbStructure + .Table.DOWNLOADS, + new String[]{DbStructure.Column.WATCHED}, + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}, null, Integer.class); op.setCallback(new DataCallback>() { @Override public void onResult(List ordinals) { - if(ordinals!=null && !ordinals.isEmpty()){ + if (ordinals != null && !ordinals.isEmpty()) { dataCallback.sendResult(WatchedState.values()[ordinals.get(0)]); - }else{ + } else { dataCallback.sendResult(WatchedState.UNWATCHED); } } @@ -566,20 +667,20 @@ public void onFail(Exception ex) { @Override public Integer getVideoCountByVideoUrl(String videoUrl, DataCallback callback) { - DbOperationGetCount op = new DbOperationGetCount(false,DbStructure.Table.DOWNLOADS, - new String[] {DbStructure.Column.VIDEO_ID}, - DbStructure.Column.URL + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { videoUrl, username() }, null); + DbOperationGetCount op = new DbOperationGetCount(false, DbStructure.Table.DOWNLOADS, + new String[]{DbStructure.Column.VIDEO_ID}, + DbStructure.Column.URL + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoUrl, username()}, null); op.setCallback(callback); return enqueue(op); } @Override public VideoModel getDownloadEntryByDmId(long dmId, - DataCallback callback) { - DbOperationGetVideo op = new DbOperationGetVideo(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.DM_ID + "=? AND "+ DbStructure.Column.DOWNLOADED + "=?" , - new String[] { String.valueOf(dmId), String.valueOf(DownloadedState + DataCallback callback) { + DbOperationGetVideo op = new DbOperationGetVideo(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.DM_ID + "=? AND " + DbStructure.Column.DOWNLOADED + "=?", + new String[]{String.valueOf(dmId), String.valueOf(DownloadedState .DOWNLOADING.ordinal())}, null); op.setCallback(callback); return enqueue(op); @@ -591,26 +692,30 @@ public Boolean isVideoDownloadingByVideoId(String videoId, DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, new String[] {DbStructure.Column.VIDEO_ID}, DbStructure.Column.VIDEO_ID + "=? " - + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + "=?", - new String[] { videoId, String.valueOf(DownloadedState.DOWNLOADING.ordinal()) + + DbStructure.Column.DOWNLOADED + "=? AND " + DbStructure.Column.USERNAME + + "=?", + new String[] { videoId, String.valueOf(DownloadedState.DOWNLOADING + .ordinal()) ,username()}, null); op.setCallback(callback); return enqueue(op); }*/ - + @Override public DownloadedState getDownloadedStateForVideoId(String videoId, - final DataCallback dataCallback) { - DbOperationGetColumn op = new DbOperationGetColumn(false,DbStructure.Table.DOWNLOADS, - new String[] { DbStructure.Column.DOWNLOADED }, - DbStructure.Column.VIDEO_ID + "=? AND "+ DbStructure.Column.USERNAME + "=?" , - new String[] { videoId, username()}, null, Integer.class); + final DataCallback + dataCallback) { + DbOperationGetColumn op = new DbOperationGetColumn(false, DbStructure + .Table.DOWNLOADS, + new String[]{DbStructure.Column.DOWNLOADED}, + DbStructure.Column.VIDEO_ID + "=? AND " + DbStructure.Column.USERNAME + "=?", + new String[]{videoId, username()}, null, Integer.class); op.setCallback(new DataCallback>() { @Override public void onResult(List ordinals) { - if(ordinals!=null && !ordinals.isEmpty()){ + if (ordinals != null && !ordinals.isEmpty()) { dataCallback.sendResult(DownloadedState.values()[ordinals.get(0)]); - }else{ + } else { dataCallback.sendResult(DownloadedState.ONLINE); } } @@ -624,39 +729,47 @@ public void onFail(Exception ex) { } @Override - public Boolean isAnyVideoDownloadingInCourse(final DataCallback callback, String courseId) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.EID + "=? AND "+ DbStructure.Column.USERNAME + "=? AND " + public Boolean isAnyVideoDownloadingInCourse(final DataCallback callback, String + courseId) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.USERNAME + "=? AND " + DbStructure.Column.DOWNLOADED + "=?", - new String[] {courseId, username(), String.valueOf(DownloadedState.DOWNLOADING.ordinal()) }, null); + new String[]{courseId, username(), String.valueOf(DownloadedState.DOWNLOADING + .ordinal())}, null); op.setCallback(callback); return enqueue(op); } @Override - public Boolean isAnyVideoDownloadingInSection(final DataCallback callback, String courseId, String section) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.EID + "=? AND "+ DbStructure.Column.CHAPTER + "=? AND "+ - DbStructure.Column.USERNAME + "=? AND "+ DbStructure.Column.DOWNLOADED + "=?", - new String[] {courseId, section, username(), String.valueOf(DownloadedState.DOWNLOADING.ordinal()) }, null); + public Boolean isAnyVideoDownloadingInSection(final DataCallback callback, String + courseId, String section) { + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.USERNAME + "=? AND " + DbStructure.Column.DOWNLOADED + + "=?", + new String[]{courseId, section, username(), String.valueOf(DownloadedState + .DOWNLOADING.ordinal())}, null); op.setCallback(callback); return enqueue(op); } @Override - public Boolean isAnyVideoDownloadingInSubSection(final DataCallback callback, String courseId, + public Boolean isAnyVideoDownloadingInSubSection(final DataCallback callback, String + courseId, String section, String subSection) { - DbOperationExists op = new DbOperationExists(false,DbStructure.Table.DOWNLOADS, null, - DbStructure.Column.EID + "=? AND "+ DbStructure.Column.CHAPTER + "=? AND "+ - DbStructure.Column.SECTION + "=? AND "+ DbStructure.Column.USERNAME + "=? AND "+ + DbOperationExists op = new DbOperationExists(false, DbStructure.Table.DOWNLOADS, null, + DbStructure.Column.EID + "=? AND " + DbStructure.Column.CHAPTER + "=? AND " + + DbStructure.Column.SECTION + "=? AND " + DbStructure.Column.USERNAME + + "=? AND " + DbStructure.Column.DOWNLOADED + "=?", - new String[] {courseId, section, subSection, username(), String.valueOf(DownloadedState.DOWNLOADING.ordinal()) }, null); + new String[]{courseId, section, subSection, username(), String.valueOf + (DownloadedState.DOWNLOADING.ordinal())}, null); op.setCallback(callback); return enqueue(op); } @Override - public void setUserName(String userName){ + public void setUserName(String userName) { this.username = userName; } @@ -664,13 +777,15 @@ public void setUserName(String userName){ /** * update assessment unit access record */ - public synchronized Integer updateAccess(DataCallback callback, String unitId, boolean visited){ + public synchronized Integer updateAccess(DataCallback callback, String unitId, + boolean visited) { ContentValues values = new ContentValues(); values.put(DbStructure.Column.ASSESSMENT_TB_UNIT_WATCHED, visited); DbOperationUpdate op = new DbOperationUpdate(DbStructure.Table.ASSESSMENT, values, - DbStructure.Column.ASSESSMENT_TB_UNIT_ID + "=? AND " + DbStructure.Column.ASSESSMENT_TB_USERNAME + "=?", - new String[] {unitId, username()}); + DbStructure.Column.ASSESSMENT_TB_UNIT_ID + "=? AND " + DbStructure.Column + .ASSESSMENT_TB_USERNAME + "=?", + new String[]{unitId, username()}); op.setCallback(callback); return enqueue(op); } @@ -678,17 +793,20 @@ public synchronized Integer updateAccess(DataCallback callback, String /** * get assessment unit access status */ - public synchronized boolean isUnitAccessed(final DataCallback callback, String unitId){ - DbOperationGetColumn op = new DbOperationGetColumn(false,DbStructure.Table.ASSESSMENT, - new String[] { DbStructure.Column.ASSESSMENT_TB_UNIT_WATCHED }, - DbStructure.Column.ASSESSMENT_TB_UNIT_ID + "=? AND "+ DbStructure.Column.ASSESSMENT_TB_USERNAME + "=?" , - new String[] { unitId, username()}, null, Boolean.class); - if( callback != null ) { + public synchronized boolean isUnitAccessed(final DataCallback callback, String + unitId) { + DbOperationGetColumn op = new DbOperationGetColumn(false, DbStructure + .Table.ASSESSMENT, + new String[]{DbStructure.Column.ASSESSMENT_TB_UNIT_WATCHED}, + DbStructure.Column.ASSESSMENT_TB_UNIT_ID + "=? AND " + DbStructure.Column + .ASSESSMENT_TB_USERNAME + "=?", + new String[]{unitId, username()}, null, Boolean.class); + if (callback != null) { op.setCallback(new DataCallback>() { @Override public void onResult(List ordinals) { if (ordinals != null && !ordinals.isEmpty()) { - callback.sendResult( ordinals.get(0)); + callback.sendResult(ordinals.get(0)); } else { //if no record, it also means not accessed before. callback.sendResult(false); diff --git a/VideoLocker/src/main/java/org/edx/mobile/module/download/DownloadCompleteReceiver.java b/VideoLocker/src/main/java/org/edx/mobile/module/download/DownloadCompleteReceiver.java index cde8bbd938..190414ff3c 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/module/download/DownloadCompleteReceiver.java +++ b/VideoLocker/src/main/java/org/edx/mobile/module/download/DownloadCompleteReceiver.java @@ -7,7 +7,6 @@ import com.google.inject.Inject; import org.edx.mobile.core.IEdxEnvironment; -import org.edx.mobile.event.DownloadEvent; import org.edx.mobile.logger.Logger; import org.edx.mobile.model.VideoModel; import org.edx.mobile.model.db.DownloadEntry; @@ -16,7 +15,6 @@ import org.edx.mobile.module.db.DataCallback; import org.edx.mobile.module.prefs.PrefManager; -import de.greenrobot.event.EventBus; import roboguice.receiver.RoboBroadcastReceiver; public class DownloadCompleteReceiver extends RoboBroadcastReceiver { @@ -54,18 +52,6 @@ public void onResult(VideoModel result) { ISegment segIO = environment.getSegment(); segIO.trackDownloadComplete(download.videoId, download.eid, download.lmsUrl); - - // update count of downloaded videos - // store user's data in his own preference file, so as to keep it unique - PrefManager p = new PrefManager(context, download.username); - long count = p.getLong(PrefManager.Key.COUNT_OF_VIDEOS_DOWNLOADED); - if (count < 0) { - count = 0; - } - count ++; - p.put(PrefManager.Key.COUNT_OF_VIDEOS_DOWNLOADED, count); - - EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.COMPLETED)); } } diff --git a/VideoLocker/src/main/java/org/edx/mobile/module/prefs/PrefManager.java b/VideoLocker/src/main/java/org/edx/mobile/module/prefs/PrefManager.java index 2b519ba3df..74a6b86ec2 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/module/prefs/PrefManager.java +++ b/VideoLocker/src/main/java/org/edx/mobile/module/prefs/PrefManager.java @@ -339,7 +339,6 @@ public static final class Key { public static final String AUTH_TOKEN_SOCIAL_COOKIE = "social_auth_cookie"; public static final String DOWNLOAD_ONLY_ON_WIFI = "download_only_on_wifi"; public static final String DOWNLOAD_OFF_WIFI_SHOW_DIALOG_FLAG = "download_off_wifi_dialog_flag"; - public static final String COUNT_OF_VIDEOS_DOWNLOADED = "count_videos_downloaded"; public static final String TRANSCRIPT_LANGUAGE = "transcript_language"; public static final String ALLOW_SOCIAL_FEATURES = "allow_social_features"; public static final String LAST_ACCESS_MODIFICATION_TIME = "last_access_modification_time"; diff --git a/VideoLocker/src/main/java/org/edx/mobile/module/storage/Storage.java b/VideoLocker/src/main/java/org/edx/mobile/module/storage/Storage.java index 40110775eb..22b9afb1ee 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/module/storage/Storage.java +++ b/VideoLocker/src/main/java/org/edx/mobile/module/storage/Storage.java @@ -219,19 +219,15 @@ public void onFail(Exception ex) { @Override public void getAverageDownloadProgressInSection(String enrollmentId, String chapter, String section, DataCallback callback) { - List dmidList = db.getDownloadingVideoDmIdsForSection(enrollmentId, chapter, section, null); - if (dmidList == null || dmidList.isEmpty()) { - callback.onResult(Integer.valueOf(0)); + long[] dmidArray = db.getDownloadingVideoDmIdsForSection(enrollmentId, chapter, section, null); + if (dmidArray == null || dmidArray.length == 0) { + callback.onResult(0); return; } try { - long[] dmidArray = new long[dmidList.size()]; - for (int i=0; i< dmidList.size(); i++) { - dmidArray[i] = dmidList.get(i); - } int progress = dm.getAverageProgressForDownloads(dmidArray); - callback.sendResult(Integer.valueOf(progress)); + callback.sendResult((int)progress); } catch(Exception ex) { logger.error(ex); callback.sendException(ex); diff --git a/VideoLocker/src/main/java/org/edx/mobile/player/PlayerController.java b/VideoLocker/src/main/java/org/edx/mobile/player/PlayerController.java index b648e6326c..65f85a5a3d 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/player/PlayerController.java +++ b/VideoLocker/src/main/java/org/edx/mobile/player/PlayerController.java @@ -534,12 +534,12 @@ public void updatePausePlay() { } if (mPlayer.isPlaying()) { mPauseButton.setImageDrawable(new IconDrawable(getContext(), Iconify.IconValue.fa_pause) - .colorRes(R.color.edx_grayscale_neutral_white_t)); + .colorRes(getContext(), R.color.edx_grayscale_neutral_white_t)); mPauseButton.setContentDescription(getContext().getResources() .getString(R.string.video_player_pause)); } else { mPauseButton.setImageDrawable(new IconDrawable(getContext(), Iconify.IconValue.fa_play) - .colorRes(R.color.edx_grayscale_neutral_white_t)); + .colorRes(getContext(),R.color.edx_grayscale_neutral_white_t)); mPauseButton.setContentDescription(getContext().getResources() .getString(R.string.video_player_play)); } diff --git a/VideoLocker/src/main/java/org/edx/mobile/services/VideoDownloadHelper.java b/VideoLocker/src/main/java/org/edx/mobile/services/VideoDownloadHelper.java index b8d491d0be..cf82cc4924 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/services/VideoDownloadHelper.java +++ b/VideoLocker/src/main/java/org/edx/mobile/services/VideoDownloadHelper.java @@ -30,7 +30,7 @@ */ @Singleton public class VideoDownloadHelper { - public static interface DownloadManagerCallback{ + public interface DownloadManagerCallback{ void onDownloadSuccess(Long result); void onDownloadFailure(); void showProgressDialog(int numDownloads); diff --git a/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconDrawable.java b/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconDrawable.java index 19caa5ac39..a6223349ca 100644 --- a/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconDrawable.java +++ b/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconDrawable.java @@ -23,24 +23,39 @@ import android.annotation.TargetApi; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; +import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.SystemClock; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.support.annotation.DimenRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextPaint; +import android.util.StateSet; import android.view.View; +import org.edx.mobile.third_party.iconify.Iconify.IconValue; + import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; -import static android.os.Build.VERSION_CODES.KITKAT; -import static java.lang.String.valueOf; + +import static android.os.Build.VERSION_CODES.LOLLIPOP; import static org.edx.mobile.third_party.iconify.Utils.convertDpToPx; /** * Embed an icon into a Drawable that can be used as TextView icons, or ActionBar icons. - *

*

  *     new IconDrawable(context, IconValue.icon_star)
  *           .colorRes(R.color.white)
@@ -50,47 +65,68 @@
  * that is given to him. Note that in an ActionBar, if you don't
  * set the size explicitly it uses 0, so please use actionBarSize().
  */
-public class IconDrawable extends Drawable {
-
-    public static final int ANDROID_ACTIONBAR_ICON_SIZE_DP = 24;
-
-    private final Context context;
-
-    private final Iconify.IconValue icon;
-
-    private TextPaint paint;
-
-    private int size = -1;
-
-    private int alpha = 255;
-
-    private boolean autoMirrored;
+public final class IconDrawable extends Drawable implements Animatable {
+    static final int DEFAULT_COLOR = Color.BLACK;
+    // Set the default tint to make it half translucent on disabled state.
+    private static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.MULTIPLY;
+    private static final ColorStateList DEFAULT_TINT = new ColorStateList(
+            new int[][] { { -android.R.attr.state_enabled }, StateSet.WILD_CARD },
+            new int[] { 0x80FFFFFF, 0xFFFFFFFF }
+    );
+    private static final int ROTATION_DURATION = 2000;
+    private static final int ANDROID_ACTIONBAR_ICON_SIZE_DP = 24;
+    private static final Rect TEMP_DRAW_BOUNDS = new Rect();
+
+    @NonNull
+    private IconState iconState;
+    private final TextPaint paint;
+    private int color;
+    private ColorFilter tintFilter;
+    private int tintColor;
+    private long rotationStartTime = -1;
+    private boolean mMutated;
+    private final String text;
+    private final Rect drawBounds = new Rect();
+    private float centerX, centerY;
 
     /**
      * Create an IconDrawable.
      * @param context Your activity or application context.
      * @param icon    The icon you want this drawable to display.
      */
-    public IconDrawable(Context context, Iconify.IconValue icon) {
-        this.context = context;
-        this.icon = icon;
-        paint = new TextPaint();
+    public IconDrawable(@NonNull Context context, @NonNull IconValue icon) {
+        this(context, new IconState(icon));
+    }
+
+    private IconDrawable(@NonNull IconState state) {
+        this(null, state);
+    }
+
+    private IconDrawable(Context context, @NonNull IconState state) {
+        iconState = state;
+        paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
         paint.setTypeface(Iconify.getTypeface(context));
-        paint.setStyle(Paint.Style.FILL);
+        paint.setStyle(state.style);
         paint.setTextAlign(Paint.Align.CENTER);
         paint.setUnderlineText(false);
-        paint.setColor(Color.BLACK);
-        paint.setAntiAlias(true);
-        // Default to enable auto-mirroring
-        setAutoMirrored(true);
+        color = state.colorStateList.getColorForState(StateSet.WILD_CARD, DEFAULT_COLOR);
+        paint.setColor(color);
+        updateTintFilter();
+        setModulatedAlpha();
+        paint.setDither(iconState.dither);
+        text = String.valueOf(iconState.icon.character());
+        if (SDK_INT < LOLLIPOP && iconState.bounds != null) {
+            setBounds(iconState.bounds);
+        }
     }
 
     /**
      * Set the size of this icon to the standard Android ActionBar.
      * @return The current IconDrawable for chaining.
      */
-    public IconDrawable actionBarSize() {
-        return sizeDp(ANDROID_ACTIONBAR_ICON_SIZE_DP);
+    @NonNull
+    public IconDrawable actionBarSize(@NonNull Context context) {
+        return sizeDp(context, ANDROID_ACTIONBAR_ICON_SIZE_DP);
     }
 
     /**
@@ -98,7 +134,8 @@ public IconDrawable actionBarSize() {
      * @param dimenRes The dimension resource.
      * @return The current IconDrawable for chaining.
      */
-    public IconDrawable sizeRes(int dimenRes) {
+    @NonNull
+    public IconDrawable sizeRes(@NonNull Context context, @DimenRes int dimenRes) {
         return sizePx(context.getResources().getDimensionPixelSize(dimenRes));
     }
 
@@ -107,7 +144,8 @@ public IconDrawable sizeRes(int dimenRes) {
      * @param size The size in density-independent pixels (dp).
      * @return The current IconDrawable for chaining.
      */
-    public IconDrawable sizeDp(int size) {
+    @NonNull
+    public IconDrawable sizeDp(@NonNull Context context, int size) {
         return sizePx(convertDpToPx(context, size));
     }
 
@@ -116,10 +154,12 @@ public IconDrawable sizeDp(int size) {
      * @param size The size in pixels (px).
      * @return The current IconDrawable for chaining.
      */
+    @NonNull
     public IconDrawable sizePx(int size) {
-        this.size = size;
-        setBounds(0, 0, size, size);
-        invalidateSelf();
+        iconState.height = size;
+        paint.setTextSize(size);
+        paint.getTextBounds(text, 0, 1, TEMP_DRAW_BOUNDS);
+        iconState.width = TEMP_DRAW_BOUNDS.width();
         return this;
     }
 
@@ -128,9 +168,24 @@ public IconDrawable sizePx(int size) {
      * @param color The color, usually from android.graphics.Color or 0xFF012345.
      * @return The current IconDrawable for chaining.
      */
-    public IconDrawable color(int color) {
-        paint.setColor(color);
-        invalidateSelf();
+    @NonNull
+    public IconDrawable color(@ColorInt int color) {
+        return color(ColorStateList.valueOf(color));
+    }
+
+    /**
+     * Set the color of the drawable.
+     * @param colorStateList The color state list.
+     * @return The current IconDrawable for chaining.
+     */
+    @NonNull
+    public IconDrawable color(@NonNull ColorStateList colorStateList) {
+        if (colorStateList != iconState.colorStateList) {
+            iconState.colorStateList = colorStateList;
+            color = iconState.colorStateList.getColorForState(StateSet.WILD_CARD, DEFAULT_COLOR);
+            paint.setColor(color);
+            invalidateSelf();
+        }
         return this;
     }
 
@@ -139,10 +194,12 @@ public IconDrawable color(int color) {
      * @param colorRes The color resource, from your R file.
      * @return The current IconDrawable for chaining.
      */
-    public IconDrawable colorRes(int colorRes) {
-        paint.setColor(context.getResources().getColor(colorRes));
-        invalidateSelf();
-        return this;
+    @NonNull
+    public IconDrawable colorRes(@NonNull Context context, @ColorRes int colorRes) {
+        // Since we have an @ColorRes annotation on the colorRes parameter,
+        // we can be sure that we will get a non-null ColorStateList.
+        //noinspection ConstantConditions
+        return color(context.getResources().getColorStateList(colorRes));
     }
 
     /**
@@ -150,9 +207,20 @@ public IconDrawable colorRes(int colorRes) {
      * @param alpha The alpha, between 0 (transparent) and 255 (opaque).
      * @return The current IconDrawable for chaining.
      */
+    @NonNull
     public IconDrawable alpha(int alpha) {
         setAlpha(alpha);
-        invalidateSelf();
+        return this;
+    }
+
+    /**
+     * Start a spinning animation on this drawable. Call {@link #stop()}
+     * to stop it.
+     * @return The current IconDrawable for chaining.
+     */
+    @NonNull
+    public IconDrawable rotate() {
+        start();
         return this;
     }
 
@@ -160,151 +228,367 @@ public IconDrawable alpha(int alpha) {
      * Returns the icon to be displayed
      * @return The icon
      */
-    public final Iconify.IconValue getIcon() {
-        return icon;
+    public final IconValue getIcon() {
+        return iconState.icon;
     }
 
     @Override
     public int getIntrinsicHeight() {
-        return size;
+        return iconState.height;
     }
 
     @Override
     public int getIntrinsicWidth() {
-        return size;
+        return iconState.width;
     }
 
     @Override
-    public void draw(Canvas canvas) {
-        Rect bounds = getBounds();
-        int height = bounds.height();
+    protected void onBoundsChange(@NonNull Rect bounds) {
+        final int width = bounds.width();
+        final int height = bounds.height();
         paint.setTextSize(height);
-        Rect textBounds = new Rect();
-        String textValue = valueOf(icon.character);
-        paint.getTextBounds(textValue, 0, 1, textBounds);
-        int textHeight = textBounds.height();
-        float textBottom = bounds.top + (height - textHeight) / 2f + textHeight - textBounds.bottom;
+        paint.getTextBounds(text, 0, 1, drawBounds);
+        paint.setTextSize(Math.min(height, (int) Math.ceil(
+                width * (height / (float) drawBounds.width()))));
+        paint.getTextBounds(text, 0, 1, drawBounds);
+        drawBounds.offsetTo(bounds.left + (width - drawBounds.width()) / 2,
+                bounds.top + (height - drawBounds.height()) / 2 - drawBounds.bottom);
+        centerX = bounds.exactCenterX();
+        centerY = bounds.exactCenterY();
+    }
+
+    @Override
+    @NonNull
+    public Rect getDirtyBounds() {
+        return drawBounds;
+    }
+
+    @Override
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public void getOutline(@NonNull Outline outline) {
+        outline.setRect(drawBounds);
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        canvas.save();
         final boolean needMirroring = needMirroring();
         if (needMirroring) {
-            canvas.save();
-            // Mirror the icon
-            canvas.translate(bounds.width(), 0);
+            canvas.translate(getBounds().width(), 0);
             canvas.scale(-1.0f, 1.0f);
         }
-        canvas.drawText(textValue, bounds.exactCenterX(), textBottom, paint);
-        if (needMirroring) {
-            canvas.restore();
+        if (iconState.rotating) {
+            long currentTime = SystemClock.uptimeMillis();
+            if (rotationStartTime < 0) {
+                rotationStartTime = currentTime;
+            } else {
+                float rotation = (currentTime - rotationStartTime) /
+                        (float) ROTATION_DURATION * 360f;
+                canvas.rotate(rotation, centerX, centerY);
+            }
+            if (isVisible()) {
+                invalidateSelf();
+            }
         }
+        canvas.drawText(text, centerX, drawBounds.bottom, paint);
+        canvas.restore();
     }
 
     @Override
     public boolean isStateful() {
-        return true;
+        return iconState.colorStateList.isStateful() ||
+                (iconState.tint != null && iconState.tint.isStateful());
     }
 
     @Override
-    public void setAlpha(int alpha) {
-        this.alpha = alpha;
-        paint.setAlpha(alpha);
+    protected boolean onStateChange(@NonNull int[] state) {
+        boolean changed = false;
+
+        int newColor = iconState.colorStateList.getColorForState(state, DEFAULT_COLOR);
+        if (newColor != color) {
+            color = newColor;
+            paint.setColor(color);
+            setModulatedAlpha();
+            changed = true;
+        }
+
+        if (tintFilter != null) {
+            // If tintFilter is not null, then it's guaranteed that tint and tintMode
+            // are not null as well, so suppress any warnings otherwise.
+            //noinspection ConstantConditions
+            int newTintColor = iconState.tint.getColorForState(state, Color.TRANSPARENT);
+            if (newTintColor != tintColor) {
+                tintColor = newTintColor;
+                //noinspection ConstantConditions
+                tintFilter = new PorterDuffColorFilter(tintColor, iconState.tintMode);
+                if (iconState.colorFilter == null) {
+                    paint.setColorFilter(tintFilter);
+                    changed = true;
+                }
+            }
+        }
+
+        return changed;
     }
 
     @Override
-    public void setColorFilter(ColorFilter cf) {
-        paint.setColorFilter(cf);
+    public void setAlpha(int alpha) {
+        if (alpha != iconState.alpha) {
+            iconState.alpha = alpha;
+            setModulatedAlpha();
+            invalidateSelf();
+        }
+    }
+
+    private void setModulatedAlpha() {
+        paint.setAlpha(((color >> 24) * iconState.alpha) / 255);
     }
 
     @Override
-    public void clearColorFilter() {
-        paint.setColorFilter(null);
+    public int getAlpha() {
+        return iconState.alpha;
     }
 
     @Override
     public int getOpacity() {
-        return this.alpha;
+        int baseAlpha = color >> 24;
+        if (baseAlpha == 255 && iconState.alpha == 255) return PixelFormat.OPAQUE;
+        if (baseAlpha == 0 || iconState.alpha == 0) return PixelFormat.TRANSPARENT;
+        return PixelFormat.OPAQUE;
+    }
+
+    @Override
+    public void setDither(boolean dither) {
+        if (dither != iconState.dither) {
+            iconState.dither = dither;
+            paint.setDither(dither);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter cf) {
+        if (cf != iconState.colorFilter) {
+            iconState.colorFilter = cf;
+            paint.setColorFilter(cf);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    @Nullable
+    public ColorFilter getColorFilter() {
+        return iconState.colorFilter;
+    }
+
+    @Override
+    public void setTintList(@Nullable ColorStateList tint) {
+        if (tint != iconState.tint) {
+            iconState.tint = tint;
+            updateTintFilter();
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
+        if (tintMode != iconState.tintMode) {
+            iconState.tintMode = tintMode;
+            updateTintFilter();
+            invalidateSelf();
+        }
+    }
+
+    private void updateTintFilter() {
+        if (iconState.tint == null || iconState.tintMode == null) {
+            if (tintFilter == null) {
+                return;
+            }
+            tintColor = 0;
+            tintFilter = null;
+        } else {
+            tintColor = iconState.tint.getColorForState(getState(), Color.TRANSPARENT);
+            tintFilter = new PorterDuffColorFilter(tintColor, iconState.tintMode);
+        }
+        if (iconState.colorFilter == null) {
+            paint.setColorFilter(tintFilter);
+            invalidateSelf();
+        }
     }
 
     /**
      * Sets paint style.
      * @param style to be applied
      */
-    public void setStyle(Paint.Style style) {
-        paint.setStyle(style);
+    public void setStyle(@NonNull Paint.Style style) {
+        if (style != iconState.style) {
+            iconState.style = style;
+            paint.setStyle(style);
+            invalidateSelf();
+        }
     }
 
     @Override
     public void setAutoMirrored(boolean mirrored) {
-        if (SDK_INT >= JELLY_BEAN_MR1 && icon.supportsRtl && autoMirrored != mirrored) {
-            autoMirrored = mirrored;
+        if (SDK_INT >= JELLY_BEAN_MR1 && iconState.icon.supportsRtl &&
+                iconState.autoMirrored != mirrored) {
+            iconState.autoMirrored = mirrored;
             invalidateSelf();
         }
     }
 
     @Override
     public final boolean isAutoMirrored() {
-        return autoMirrored;
+        return iconState.autoMirrored;
     }
 
-    @TargetApi(KITKAT)
+    // Since the auto-mirrored state is only set to true the SDK version
+    // supports it, we don't need an explicit check for it before calling
+    // the getLayoutDirection() methods.
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     private boolean needMirroring() {
         if (isAutoMirrored()) {
-            // Since getLayoutDirection() is hidden, we will try to
-            // get the layout direction from the View, which we will
-            // attempt to get from the Callback. As the
-            // setLayoutDirection() method is also hidden, we can
-            // safely rely on the behaviour of the platform Views
-            // to provide a correct replacement for the hidden method.
+            // TODO: Uncomment this one we start compiling against Marshmallow
+            /*if (SDK_INT >= M) {
+                return getLayoutDirection() == LayoutDirection.RTL;
+            }*/
+            // Since getLayoutDirection() is hidden prior to Marshmallow, we will
+            // try to get the layout direction from the View, which we will assume
+            // is set as the callback. As the setLayoutDirection() method is also
+            // hidden, we can safely rely on the behaviour of the platform Views to
+            // provide a correct replacement for the hidden method.
             Callback callback = getCallback();
             if (callback instanceof View) {
-                View view = (View) callback;
-                if (SDK_INT < KITKAT || view.isLayoutDirectionResolved()) {
-                    return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-                }
+                return ((View) callback).getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             }
         }
         return false;
     }
 
-    // Although there is no shared state associated with IconDrawable, we
-    // need to provide a non-null ConstantState in order to make it work
-    // with LayerDrawable, which uses it to create a new instance of it's
-    // children when mutated, without checking if they support it. This
-    // bug has been fixed in Marshmallow. We work around it by providing
-    // a fake ConstantState which is actually bound to the IconDrawable
-    // instance.
-    private IconState state;
+    @Override
+    public void start() {
+        if (!iconState.rotating) {
+            iconState.rotating = true;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (iconState.rotating) {
+            iconState.rotating = false;
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return iconState.rotating;
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        final boolean changed = super.setVisible(visible, restart);
+        if (iconState.rotating) {
+            if (changed) {
+                if (visible) {
+                    invalidateSelf();
+                }
+            } else {
+                if (restart && visible) {
+                    rotationStartTime = -1;
+                }
+            }
+        }
+        return changed;
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return iconState.changingConfigurations;
+    }
+
+    @Override
+    public void setChangingConfigurations(int configs) {
+        iconState.changingConfigurations = configs;
+    }
 
+    // Implementing shared state despite being a third-party implementation
+    // in order to work around bugs in the framework and support library:
+    // http://b.android.com/191754
+    // https://github.com/JoanZapata/android-iconify/issues/93
     @Override
     public ConstantState getConstantState() {
-        if (state == null) {
-            state = new IconState();
+        // The bounds level need to be copied here to work around a bug in
+        // LayerDrawable where it doesn't copy the bounds and level in it's
+        // children when mutated or cloned. This bug has been fixed in
+        // Lollipop. The layout direction was not copied as well in Jelly
+        // Bean MR 1, but we're ignoring it for the moment as it's not
+        // exposed in the SDK prior to Marshmallow. If it ever becomes an
+        // issue though, then we'll need to handle that as well.
+        iconState.bounds = getBounds();
+        return iconState;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            iconState = new IconState(iconState);
+            mMutated = true;
         }
-        return state;
+        return this;
     }
 
-    protected class IconState extends ConstantState {
+    private static class IconState extends ConstantState {
+        @NonNull
+        final IconValue icon;
+        int height = -1, width = -1;
+        @NonNull
+        ColorStateList colorStateList = ColorStateList.valueOf(DEFAULT_COLOR);
+        int alpha = 255;
+        boolean dither;
+        @Nullable
+        ColorFilter colorFilter;
+        @Nullable
+        ColorStateList tint = DEFAULT_TINT;
+        @Nullable
+        PorterDuff.Mode tintMode = DEFAULT_TINT_MODE;
+        @NonNull
+        Paint.Style style = Paint.Style.FILL;
+        boolean rotating;
+        boolean autoMirrored;
+        int changingConfigurations;
+        @Nullable
+        Rect bounds;
+
+        IconState(@NonNull IconValue icon) {
+            this.icon = icon;
+            autoMirrored = SDK_INT >= JELLY_BEAN_MR1 && icon.supportsRtl;
+        }
+
+        IconState(IconState state) {
+            icon = state.icon;
+            height = state.height;
+            width = state.width;
+            colorStateList = state.colorStateList;
+            alpha = state.alpha;
+            dither = state.dither;
+            colorFilter = state.colorFilter;
+            tint = state.tint;
+            tintMode = state.tintMode;
+            style = state.style;
+            rotating = state.rotating;
+            autoMirrored = state.autoMirrored;
+            changingConfigurations = state.changingConfigurations;
+        }
+
         @Override
         public Drawable newDrawable() {
-            IconDrawable iconDrawable = new IconDrawable(context, icon);
-            iconDrawable.sizePx(size);
-            iconDrawable.color(paint.getColor());
-            iconDrawable.setAlpha(getAlpha());
-            iconDrawable.setColorFilter(paint.getColorFilter());
-            iconDrawable.setStyle(paint.getStyle());
-            iconDrawable.setAutoMirrored(isAutoMirrored());
-            iconDrawable.setChangingConfigurations(getChangingConfigurations());
-            // The bounds and level needs to be copied here to work around a bug
-            // in LayerDrawable where it doesn't initialize these properties in
-            // it's children when mutated or cloned. This bug has been fixed in
-            // Lollipop.
-            iconDrawable.setBounds(getBounds());
-            iconDrawable.setLevel(getLevel());
-            return iconDrawable;
+            return new IconDrawable(this);
         }
 
         @Override
         public int getChangingConfigurations() {
-            return IconDrawable.this.getChangingConfigurations();
+            return changingConfigurations;
         }
     }
-
 }
\ No newline at end of file
diff --git a/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconView.java b/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconView.java
index 39a8a5801d..dfbdebe5b8 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconView.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/third_party/iconify/IconView.java
@@ -1,9 +1,13 @@
 package org.edx.mobile.third_party.iconify;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
@@ -11,21 +15,25 @@
 
 public class IconView extends ImageView {
 
-    private int color;
+    @NonNull
+    private ColorStateList colorStateList = ColorStateList.valueOf(IconDrawable.DEFAULT_COLOR);
 
-    public IconView(Context context) {
+    public IconView(@NonNull Context context) {
         super(context);
     }
 
-    public IconView(Context context, AttributeSet attrs) {
+    public IconView(@NonNull Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public IconView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public IconView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.IconView, defStyleAttr, 0);
-        color = a.getColor(R.styleable.IconView_iconColor, Color.BLACK);
+        ColorStateList colorStateList = a.getColorStateList(R.styleable.IconView_iconColor);
+        if (colorStateList != null) {
+            this.colorStateList = colorStateList;
+        }
         if (a.hasValue(R.styleable.IconView_iconName)) {
             setIcon(Iconify.IconValue.getFromHashCode(
                     a.getInt(R.styleable.IconView_iconName, -1)));
@@ -45,20 +53,50 @@ public final Iconify.IconValue getIcon() {
         return null;
     }
 
-    public void setIconColor(int iconColor) {
-        color = iconColor;
+    public void setIconColor(@ColorInt int color) {
+        setIconColor(ColorStateList.valueOf(color));
+    }
+
+    public void setIconColor(@NonNull ColorStateList colorStateList) {
+        this.colorStateList = colorStateList;
         Drawable drawable = getDrawable();
         if (drawable instanceof IconDrawable) {
-            ((IconDrawable) drawable).color(color);
+            ((IconDrawable) drawable).color(colorStateList);
         }
     }
 
+    public void setIconColorResource(@ColorRes int colorResId) {
+        // Since we have an @ColorRes annotation on the colorRes parameter,
+        // we can be sure that we will get a non-null ColorStateList.
+        //noinspection ConstantConditions
+        setIconColor(getContext().getResources().getColorStateList(colorResId));
+    }
+
     @Override
     public void setImageDrawable(Drawable drawable) {
         if (drawable instanceof IconDrawable && drawable != getDrawable()) {
-            ((IconDrawable) drawable).color(color);
+            ((IconDrawable) drawable).color(colorStateList);
         }
         super.setImageDrawable(drawable);
     }
 
+    public void setRotating(boolean rotate) {
+        setRotation(rotate, false);
+    }
+
+    public void setRotation(boolean rotate, boolean restart) {
+        Drawable drawable = getDrawable();
+        if (drawable instanceof IconDrawable) {
+            IconDrawable iconDrawable = (IconDrawable) drawable;
+            if (rotate) {
+                iconDrawable.start();
+            } else {
+                iconDrawable.stop();
+            }
+            if (restart && getVisibility() == VISIBLE) {
+                iconDrawable.setVisible(true, true);
+            }
+        }
+    }
+
 }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/util/NetworkUtil.java b/VideoLocker/src/main/java/org/edx/mobile/util/NetworkUtil.java
index 3cd82653db..51b0859513 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/util/NetworkUtil.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/util/NetworkUtil.java
@@ -8,7 +8,10 @@
 import android.net.wifi.WifiManager;
 import android.telephony.TelephonyManager;
 
+import org.edx.mobile.R;
+import org.edx.mobile.base.BaseFragmentActivity;
 import org.edx.mobile.logger.Logger;
+import org.edx.mobile.module.prefs.PrefManager;
 
 import java.net.InetAddress;
 import java.net.NetworkInterface;
@@ -168,4 +171,28 @@ public static boolean isSocialFeatureFlagEnabled(Context context, Config config)
         return isSocialEnabled && (NetworkUtil.isConnectedWifi(context) || !NetworkUtil.isOnSocialDisabledNetwork(context, config));
 
     }
+
+    /**
+     * Verify that there is an active network connection on which downloading is allowed. If
+     * there is no such connection, then an appropriate message is displayed.
+     *
+     * @param activity Delegate of type {@link BaseFragmentActivity} to show proper error messages
+     * @return If downloads can be performed, returns true; else returns false.
+     */
+
+    public static boolean verifyDownloadPossible(BaseFragmentActivity activity) {
+        if (new PrefManager(activity, PrefManager.Pref.WIFI).getBoolean(PrefManager.Key
+                .DOWNLOAD_ONLY_ON_WIFI, true)) {
+            if (!isConnectedWifi(activity)) {
+                activity.showInfoMessage(activity.getString(R.string.wifi_off_message));
+                return false;
+            }
+        } else {
+            if (AppConstants.offline_flag) {
+                activity.showInfoMessage(activity.getString(R.string.network_not_connected));
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseBaseActivity.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseBaseActivity.java
index 54e2b1adcf..4fafb294c3 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseBaseActivity.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseBaseActivity.java
@@ -15,14 +15,12 @@
 
 import org.edx.mobile.R;
 import org.edx.mobile.base.BaseFragmentActivity;
-import org.edx.mobile.event.DownloadEvent;
 import org.edx.mobile.model.api.EnrolledCoursesResponse;
 import org.edx.mobile.model.course.CourseComponent;
 import org.edx.mobile.module.prefs.PrefManager;
 import org.edx.mobile.services.CourseManager;
 import org.edx.mobile.task.GetCourseStructureTask;
 import org.edx.mobile.third_party.iconify.IconDrawable;
-import org.edx.mobile.third_party.iconify.IconView;
 import org.edx.mobile.third_party.iconify.Iconify;
 import org.edx.mobile.util.AppConstants;
 import org.edx.mobile.util.BrowserUtil;
@@ -31,7 +29,6 @@
 import org.edx.mobile.view.common.TaskProcessCallback;
 import org.edx.mobile.view.custom.popup.menu.PopupMenu;
 
-import de.greenrobot.event.EventBus;
 import roboguice.inject.ContentView;
 import roboguice.inject.InjectView;
 
@@ -51,12 +48,6 @@ public abstract  class CourseBaseActivity  extends BaseFragmentActivity implemen
     @InjectView(R.id.last_access_bar)
     View lastAccessBar;
 
-    @InjectView(R.id.download_in_progress_bar)
-    View downloadProgressBar;
-
-    @InjectView(R.id.video_download_indicator)
-    IconView downloadIndicator;
-
     @InjectView(R.id.progress_spinner)
     ProgressBar progressWheel;
 
@@ -68,8 +59,6 @@ public abstract  class CourseBaseActivity  extends BaseFragmentActivity implemen
 
     private GetCourseStructureTask getHierarchyTask;
 
-    private boolean isDestroyed;
-
     protected abstract String getUrlForWebView();
 
     protected abstract void onLoadData();
@@ -89,17 +78,7 @@ protected void onCreate(Bundle arg0) {
     }
 
     protected void initialize(Bundle arg){
-
         setApplyPrevTransitionOnRestart(true);
-        ((IconView)findViewById(R.id.video_download_indicator)).setIconColor(getResources().getColor(R.color.edx_brand_primary_light));
-        findViewById(R.id.download_in_progress_button).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                environment.getRouter().showDownloads(CourseBaseActivity.this);
-            }
-        });
-
-
         if (!(NetworkUtil.isConnected(this))) {
             AppConstants.offline_flag = true;
             invalidateOptionsMenu();
@@ -112,18 +91,8 @@ public void onClick(View v) {
     protected void onResume() {
         super.onResume();
         invalidateOptionsMenu();
-        if ( !EventBus.getDefault().isRegistered(this) )
-            EventBus.getDefault().register(this);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if ( EventBus.getDefault().isRegistered(this) )
-            EventBus.getDefault().unregister(this);
     }
 
-
     @Override
     protected void onDestroy() {
         super.onDestroy();
@@ -131,10 +100,8 @@ protected void onDestroy() {
             getHierarchyTask.cancel(true);
             getHierarchyTask = null;
         }
-        isDestroyed = true;
     }
 
-
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -184,14 +151,6 @@ protected void onPostCreate(Bundle savedInstanceState) {
         }
     }
 
-    /**
-     * callback for EventBus
-     * https://github.com/greenrobot/EventBus
-     */
-    public void onEvent(DownloadEvent event) {
-        setVisibilityForDownloadProgressView(true);
-    }
-
     @Override
     protected void onOnline() {
         offlineBar.setVisibility(View.GONE);
@@ -224,18 +183,18 @@ public boolean onPrepareOptionsMenu(Menu menu) {
         if( menu.findItem(R.id.action_share_on_web) != null)
             menu.findItem(R.id.action_share_on_web).setIcon(
                 new IconDrawable(this, Iconify.IconValue.fa_share_square_o)
-                    .actionBarSize().colorRes(R.color.edx_white));
+                    .actionBarSize(this).colorRes(this, R.color.edx_white));
         PrefManager.UserPrefManager userPrefManager = new PrefManager.UserPrefManager(this);
 
         if (  menu.findItem(R.id.action_change_mode) != null ) {
             if (userPrefManager.isUserPrefVideoModel()) {
                 menu.findItem(R.id.action_change_mode).setIcon(
                     new IconDrawable(this, Iconify.IconValue.fa_list)
-                        .actionBarSize().colorRes(R.color.edx_white));
+                        .actionBarSize(this).colorRes(this, R.color.edx_white));
             } else {
                 menu.findItem(R.id.action_change_mode).setIcon(
                     new IconDrawable(this, Iconify.IconValue.fa_film)
-                        .actionBarSize().colorRes(R.color.edx_white));
+                        .actionBarSize(this).colorRes(this, R.color.edx_white));
             }
         }
         return true;
@@ -274,12 +233,12 @@ public void changeMode(){
         // Setting checked states
         if (userPrefManager.isUserPrefVideoModel()) {
             videoOnlyItem.setChecked(true);
-            videoOnlyIcon.colorRes(R.color.cyan_4);
-            fullCourseIcon.colorRes(R.color.black);
+            videoOnlyIcon.colorRes(this, R.color.cyan_4);
+            fullCourseIcon.colorRes(this, R.color.black);
         } else {
             fullCourseItem.setChecked(true);
-            fullCourseIcon.colorRes(R.color.cyan_4);
-            videoOnlyIcon.colorRes(R.color.black);
+            fullCourseIcon.colorRes(this, R.color.cyan_4);
+            videoOnlyIcon.colorRes(this, R.color.black);
         }
 
         //registering popup with OnMenuItemClickListener
@@ -369,12 +328,6 @@ private void hideLoadingProgress(){
         }
     }
 
-
-    protected void setVisibilityForDownloadProgressView(boolean show){
-        if ( downloadProgressBar != null )
-            downloadProgressBar.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
     protected void hideLastAccessedView(View v) {
         try{
             lastAccessBar.setVisibility(View.GONE);
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionPostsThreadFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionPostsThreadFragment.java
index ee2ac0b1f5..0edf358d43 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionPostsThreadFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionPostsThreadFragment.java
@@ -83,10 +83,11 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         createNewPostTextView.setText(R.string.discussion_post_create_new_post);
+        Context context = getActivity();
         TextViewCompat.setCompoundDrawablesRelative(createNewPostTextView,
-                new IconDrawable(getActivity(), Iconify.IconValue.fa_plus_circle)
-                        .sizeRes(R.dimen.icon_view_standard_width_height)
-                        .colorRes(R.color.edx_grayscale_neutral_white_t),
+                new IconDrawable(context, Iconify.IconValue.fa_plus_circle)
+                        .sizeRes(context, R.dimen.icon_view_standard_width_height)
+                        .colorRes(context, R.color.edx_grayscale_neutral_white_t),
                 null, null, null
         );
         createNewPostLayout.setOnClickListener(new View.OnClickListener() {
@@ -111,9 +112,9 @@ public Drawable createIcon() {
                         Context context = getActivity();
                         LayerDrawable layeredIcon = new LayerDrawable(new Drawable[]{
                                 new IconDrawable(context, Iconify.IconValue.fa_long_arrow_up)
-                                        .colorRes(R.color.edx_brand_primary_base),
+                                        .colorRes(context, R.color.edx_brand_primary_base),
                                 new IconDrawable(context, Iconify.IconValue.fa_long_arrow_down)
-                                        .colorRes(R.color.edx_brand_primary_base)
+                                        .colorRes(context, R.color.edx_brand_primary_base)
                         });
                         Resources resources = context.getResources();
                         final int width = resources.getDimensionPixelSize(
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionTopicsFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionTopicsFragment.java
index 11e803a692..baa8e7bbd5 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionTopicsFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseDiscussionTopicsFragment.java
@@ -1,5 +1,6 @@
 package org.edx.mobile.view;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v4.widget.TextViewCompat;
@@ -82,10 +83,11 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
         {
             final TextView header = (TextView) inflater.inflate(R.layout.row_discussion_topic, discussionTopicsListView, false);
             header.setText(R.string.forum_post_i_am_following);
+            Context context = getActivity();
             TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(header,
-                    new IconDrawable(getActivity(), Iconify.IconValue.fa_star)
-                            .colorRes(R.color.edx_grayscale_neutral_dark)
-                            .sizeRes(R.dimen.edx_base),
+                    new IconDrawable(context, Iconify.IconValue.fa_star)
+                            .colorRes(context, R.color.edx_grayscale_neutral_dark)
+                            .sizeRes(context, R.dimen.edx_base),
                     null, null, null);
             final DiscussionTopic discussionTopic = new DiscussionTopic();
             discussionTopic.setIdentifier(DiscussionTopic.FOLLOWING_TOPICS_ID);
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineActivity.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineActivity.java
index e2fc750d76..4148241855 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineActivity.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineActivity.java
@@ -102,8 +102,9 @@ protected void onPostCreate(Bundle savedInstanceState) {
 
     @Override
     public void updateListUI() {
-        if( fragment != null )
+        if( fragment != null ) {
             fragment.reloadList();
-        fragment.updateMessageView(null);
+            fragment.updateMessageView(null);
+        }
     }
 }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineFragment.java
index f90f5d4f9d..6e933c7be2 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseOutlineFragment.java
@@ -3,7 +3,6 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -141,34 +140,39 @@ public void updateMessageView(View view){
         }
     }
 
-    private void initializeAdapter(){
+    private void initializeAdapter() {
         if (adapter == null) {
             // creating adapter just once
-            adapter = new CourseOutlineAdapter(getActivity(), environment.getDatabase(), environment.getStorage(), new CourseOutlineAdapter.DownloadListener() {
+            adapter = new CourseOutlineAdapter(getActivity(), environment.getDatabase(),
+                    environment.getStorage(), new CourseOutlineAdapter.DownloadListener() {
                 @Override
                 public void download(List models) {
-                    downloadManager.downloadVideos(
-                            (List) models, (FragmentActivity) getActivity(), (VideoDownloadHelper.DownloadManagerCallback) getActivity());
-
+                    CourseOutlineActivity activity = (CourseOutlineActivity) getActivity();
+                    if (NetworkUtil.verifyDownloadPossible(activity)) {
+                        downloadManager.downloadVideos(models, getActivity(),
+                                (VideoDownloadHelper.DownloadManagerCallback) getActivity());
+                    }
                 }
 
                 @Override
                 public void download(DownloadEntry videoData) {
-                    downloadManager.downloadVideo(
-                            videoData, (FragmentActivity) getActivity(), (VideoDownloadHelper.DownloadManagerCallback) getActivity());
+                    CourseOutlineActivity activity = (CourseOutlineActivity) getActivity();
+                    if (NetworkUtil.verifyDownloadPossible(activity)) {
+                        downloadManager.downloadVideo(videoData, activity, activity);
+                    }
+                }
+
+                @Override
+                public void viewDownloadsStatus() {
+                    environment.getRouter().showDownloads(getActivity());
                 }
             });
         }
 
-        if (!(NetworkUtil.isConnected(getActivity()))) {
-            AppConstants.offline_flag = true;
-        } else {
-            AppConstants.offline_flag = false;
-        }
+        AppConstants.offline_flag = !NetworkUtil.isConnected(getActivity());
     }
 
 
-
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/CourseVideoListActivity.java b/VideoLocker/src/main/java/org/edx/mobile/view/CourseVideoListActivity.java
index 1979056a80..21c4cd892f 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/CourseVideoListActivity.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/CourseVideoListActivity.java
@@ -5,10 +5,7 @@
 import android.os.SystemClock;
 import android.view.Menu;
 import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 
-import org.edx.mobile.R;
 import org.edx.mobile.base.MainApplication;
 import org.edx.mobile.logger.Logger;
 import org.edx.mobile.model.course.BlockPath;
@@ -122,94 +119,24 @@ protected void modeChanged(){
     }
 
     @Override
-    protected void updateDownloadProgress(final int progressPercent){
-
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                if (progressPercent < 100) {
-                    downloadProgressBar.setVisibility(View.VISIBLE);
-                    mHandler.removeCallbacks(mHideRunnable);
-                    if (downloadIndicator.getAnimation() == null) {
-                        downloadIndicator.setVisibility(View.VISIBLE);
-                        Animation animation = AnimationUtils.loadAnimation(CourseVideoListActivity.this, R.anim.rotate);
-                        downloadIndicator.startAnimation(animation);
-                    }
-                } else { //progressPercent == 100
-                    downloadIndicator.clearAnimation();
-                    downloadIndicator.setVisibility(View.INVISIBLE);
-                    mHandler.postDelayed(mHideRunnable, getResources().getInteger(R.integer.message_delay));
-                }
-            }
-        }, 500);
-    }
+    protected void updateDownloadProgress(final int progressPercent) {}
 
     @Override
-    protected void setVisibilityForDownloadProgressView(boolean show){
-         boolean visible = downloadProgressBar.getVisibility() == View.VISIBLE;
-        if (visible == show )
-            return; //do nothing
-
-        if ( show ){
-            //TODO - we pass a value less than 100 to indicate it is downloading.
-            updateDownloadProgress(0);
-        } else {
-            updateDownloadProgress(100);
-        }
-    }
+    public void showProgressDialog(int numDownloads) {}
 
     @Override
     public void onDownloadSuccess(Long result) {
-        try {
-            updateListUI();
-        }catch(Exception e){
-            logger.error(e);
-        }
+        updateListUI();
     }
 
     @Override
     public void onDownloadFailure() {
-    }
-
-    @Override
-    public void showProgressDialog(int numDownloads) {
-        setVisibilityForDownloadProgressView(true);
+        updateListUI();
     }
 
     @Override
     public abstract  void updateListUI();
 
-
-    private Runnable mHideRunnable = new Runnable() {
-        @Override
-        public void run() {
-            downloadProgressBar.setVisibility(  View.GONE );
-        }
-    };
-
-    //TODO - legacy code use one minute tick loop to sync some UI status, like
-    //total download progress. this is a simple approach, but may not be the
-    //best one.
-    protected void onTick() {
-        // this is a per second callback
-        try {
-                if(AppConstants.offline_flag){
-                    setVisibilityForDownloadProgressView(false);
-                }else{
-                    if(environment.getDatabase()!=null){
-                        boolean downloading = environment.getDatabase().isAnyVideoDownloading(null);
-                        if(!downloading){
-                            setVisibilityForDownloadProgressView(false);
-                        }else{
-                            environment.getStorage().getAverageDownloadProgress(averageProgressCallback);
-                        }
-                    }   //store not null check
-                }
-            }  catch(Exception ex) {
-            logger.error(ex);
-        }
-    }
-
     protected boolean isOnCourseOutline(){
         if (courseComponentId == null) return true;
         CourseComponent outlineComp = courseManager.getComponentById(
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/EditUserProfileFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/EditUserProfileFragment.java
index 98f56a2b95..fbdcf9116e 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/EditUserProfileFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/EditUserProfileFragment.java
@@ -1,6 +1,7 @@
 package org.edx.mobile.view;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -449,7 +450,11 @@ private static TextView createField(@NonNull LayoutInflater inflater, @NonNull V
             put("label", formattedLabel);
             put("value", formattedValue);
         }}));
-        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, null, null, new IconDrawable(parent.getContext(), Iconify.IconValue.fa_angle_right).colorRes(R.color.edx_grayscale_neutral_light).sizeDp(24), null);
+        Context context = parent.getContext();
+        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                textView, null, null, new IconDrawable(context, Iconify.IconValue.fa_angle_right)
+                        .colorRes(context, R.color.edx_grayscale_neutral_light)
+                        .sizeDp(context, 24), null);
         if (readOnly) {
             textView.setEnabled(false);
             textView.setBackgroundColor(textView.getResources().getColor(R.color.edx_grayscale_neutral_x_light));
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/FormFieldSelectFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/FormFieldSelectFragment.java
index 3a42bdcaf5..1a3aa4fa77 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/FormFieldSelectFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/FormFieldSelectFragment.java
@@ -1,6 +1,7 @@
 package org.edx.mobile.view;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
@@ -131,10 +132,11 @@ private static void addDetectedValueHeader(@NonNull ListView listView, @StringRe
             labelValueSpan.setSpan(new ForegroundColorSpan(listView.getResources().getColor(R.color.edx_grayscale_neutral_dark)), 0, labelValueSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             textView.setText(ResourceUtil.getFormattedString(listView.getContext().getResources(), labelRes, labelKey, labelValueSpan));
         }
+        Context context = textView.getContext();
         TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
-                new IconDrawable(textView.getContext(), icon)
-                        .sizeRes(R.dimen.edx_base)
-                        .colorRes(R.color.edx_grayscale_neutral_light)
+                new IconDrawable(context, icon)
+                        .sizeRes(context, R.dimen.edx_base)
+                        .colorRes(context, R.color.edx_grayscale_neutral_light)
                 , null, null, null);
         listView.addHeaderView(textView, new FormOption(labelValue, value), true);
     }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/MyVideosTabActivity.java b/VideoLocker/src/main/java/org/edx/mobile/view/MyVideosTabActivity.java
index 3e5aaa751c..33e8a600cc 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/MyVideosTabActivity.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/MyVideosTabActivity.java
@@ -19,9 +19,7 @@
 
 import org.edx.mobile.R;
 import org.edx.mobile.logger.Logger;
-import org.edx.mobile.model.api.ProfileModel;
 import org.edx.mobile.module.analytics.ISegment;
-import org.edx.mobile.module.prefs.PrefManager;
 import org.edx.mobile.player.PlayerActivity;
 import org.edx.mobile.player.VideoListFragment.VideoListCallback;
 import org.edx.mobile.util.AppConstants;
@@ -78,13 +76,8 @@ protected void onStart() {
             setTitle(getString(R.string.label_my_videos));
         }
 
-        try{
-            if(recentVideosFragment!=null){
-                recentVideosFragment.setCallback(this);
-            }
-            clearDownloadCount();
-        }catch(Exception ex){
-            logger.error(ex);
+        if (recentVideosFragment != null) {
+            recentVideosFragment.setCallback(this);
         }
     }
 
@@ -153,18 +146,6 @@ protected void restore(Bundle savedInstanceState) {
         }
     }
 
-    private void clearDownloadCount() {
-        try{
-            PrefManager p = new PrefManager(this, PrefManager.Pref.LOGIN);
-            // user specific data is stored in his own file
-            ProfileModel profile = p.getCurrentUserProfile();
-            p = new PrefManager(this, profile.username);
-            p.put(PrefManager.Key.COUNT_OF_VIDEOS_DOWNLOADED, 0);
-        }catch(Exception e){
-            logger.error(e);
-        }
-    }
-
     private void initializeTab() {
         try{
             mTabHost = (TabHost) findViewById(android.R.id.tabhost);
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/UserProfileFragment.java b/VideoLocker/src/main/java/org/edx/mobile/view/UserProfileFragment.java
index 80c236ec64..77bc73df2e 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/UserProfileFragment.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/UserProfileFragment.java
@@ -1,5 +1,6 @@
 package org.edx.mobile.view;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -83,9 +84,10 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
         if (isViewingOwnProfile) {
             inflater.inflate(R.menu.edit_profile, menu);
+            Context context = getActivity();
             menu.findItem(R.id.edit_profile).setIcon(
-                    new IconDrawable(getActivity(), Iconify.IconValue.fa_pencil)
-                            .actionBarSize().colorRes(R.color.edx_white));
+                    new IconDrawable(context, Iconify.IconValue.fa_pencil)
+                            .actionBarSize(context).colorRes(context, R.color.edx_white));
         }
     }
 
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/CourseOutlineAdapter.java b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/CourseOutlineAdapter.java
index a708dcbfb3..73192db60d 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/CourseOutlineAdapter.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/CourseOutlineAdapter.java
@@ -12,7 +12,6 @@
 
 import org.edx.mobile.R;
 import org.edx.mobile.base.MainApplication;
-import org.edx.mobile.event.DownloadEvent;
 import org.edx.mobile.logger.Logger;
 import org.edx.mobile.model.course.BlockPath;
 import org.edx.mobile.model.course.BlockType;
@@ -21,21 +20,17 @@
 import org.edx.mobile.model.course.IBlock;
 import org.edx.mobile.model.course.VideoBlockModel;
 import org.edx.mobile.model.db.DownloadEntry;
-import org.edx.mobile.model.download.NativeDownloadModel;
 import org.edx.mobile.module.db.DataCallback;
 import org.edx.mobile.module.db.IDatabase;
 import org.edx.mobile.module.prefs.PrefManager;
 import org.edx.mobile.module.storage.IStorage;
 import org.edx.mobile.third_party.iconify.IconView;
 import org.edx.mobile.third_party.iconify.Iconify;
-import org.edx.mobile.util.AppConstants;
 import org.edx.mobile.view.custom.ETextView;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import de.greenrobot.event.EventBus;
-
 /**
  * Used for pinned behavior.
  */
@@ -46,6 +41,7 @@ public class CourseOutlineAdapter extends BaseAdapter{
     public interface DownloadListener {
         void download(List models);
         void download(DownloadEntry videoData);
+        void viewDownloadsStatus();
     }
 
     private CourseComponent rootComponent;
@@ -110,7 +106,8 @@ public final View getView(int position, View convertView, ViewGroup parent) {
 
         int type = getItemViewType(position);
 
-        if (convertView == null) {
+        // FIXME: Re-enable row recycling in favor of better DB communication [MA-1640]
+        //if (convertView == null) {
             switch (type) {
                 case SectionRow.ITEM: {
                     convertView = mInflater.inflate(R.layout.row_course_outline_list, parent, false);
@@ -124,7 +121,7 @@ public final View getView(int position, View convertView, ViewGroup parent) {
                     break;
                 }
             }
-        }
+        //}
 
         switch (type) {
             case SectionRow.ITEM:
@@ -175,7 +172,7 @@ public void setData(CourseComponent component){
 
     public void reloadData(){
         if (  this.rootComponent != null )
-            setData(this.rootComponent);
+             setData(this.rootComponent);
     }
 
     public View getRowView(int position, View convertView, ViewGroup parent) {
@@ -223,7 +220,7 @@ private  View getRowViewForLeaf(int position, View convertView, ViewGroup parent
         if ( !unit.isMultiDevice() && unit.getType() != BlockType.VIDEO){
             viewHolder.bulkDownload.setVisibility(View.INVISIBLE);
             viewHolder.rowType.setIcon(Iconify.IconValue.fa_laptop);
-            viewHolder.rowType.setIconColor(context.getResources().getColor(R.color.edx_grayscale_neutral_base));
+            viewHolder.rowType.setIconColorResource(R.color.edx_grayscale_neutral_base);
         } else if (row.component instanceof VideoBlockModel){
             updateUIForVideo(position, convertView, viewHolder, row);
         } else {
@@ -252,9 +249,9 @@ private void checkAccessStatus(final ViewHolder viewHolder, final CourseComponen
             @Override
             public void onResult(Boolean accessed) {
                 if (accessed) {
-                    viewHolder.rowType.setIconColor(context.getResources().getColor(R.color.edx_grayscale_neutral_base));
+                    viewHolder.rowType.setIconColorResource(R.color.edx_grayscale_neutral_base);
                 } else {
-                    viewHolder.rowType.setIconColor(context.getResources().getColor(R.color.edx_brand_primary_base));
+                    viewHolder.rowType.setIconColorResource(R.color.edx_brand_primary_base);
                 }
             }
 
@@ -278,85 +275,63 @@ private void updateUIForVideo(int position, View convertView, final ViewHolder v
         viewHolder.rowSubtitle.setVisibility(View.VISIBLE);
         viewHolder.rowSubtitle.setText(videoData.getDurationReadable());
 
-        if (videoData.downloaded == DownloadEntry.DownloadedState.DOWNLOADING) {
-
-            NativeDownloadModel downloadModel = storage.
-                getNativeDownlaod(videoData.dmId);
-            if(downloadModel!=null){
-                int percent = downloadModel.getPercent();
-                if(percent>=0 && percent < 100){
-                    EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.STARTED));
-                }
-            }
-        }
-
         dbStore.getWatchedStateForVideoId(videoData.videoId,
-            new DataCallback(true) {
-                @Override
-                public void onResult(DownloadEntry.WatchedState result) {
-                    if(result != null && result == DownloadEntry.WatchedState.WATCHED) {
-                        viewHolder.rowType.setIconColor(context.getResources().getColor(R.color.edx_grayscale_neutral_base));
-                    } else {
-                        viewHolder.rowType.setIconColor(context.getResources().getColor(R.color.edx_brand_primary_base));
-                    }
-                }
-                @Override
-                public void onFail(Exception ex) {
-                    logger.error(ex);
-                }
-            });
-
-        dbStore.getDownloadedStateForVideoId(videoData.videoId,
-                new DataCallback(true) {
+                new DataCallback(true) {
                     @Override
-                    public void onResult(DownloadEntry.DownloadedState result) {
-                        DownloadEntry.DownloadedState ds = result;
-                        if (ds == null || ds == DownloadEntry.DownloadedState.ONLINE) {
-                            // not yet downloaded
-                            viewHolder.bulkDownload.setVisibility(View.VISIBLE);
-                            viewHolder.numOfVideoAndDownloadArea.setOnClickListener(new View.OnClickListener() {
-                                @Override
-                                public void onClick(View v) {
-                                    EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.STARTED));
-                                    logger.debug("Download Button Clicked");
-                                    //notifyDataSetChanged();
-                                    mDownloadListener.download(videoData);
-                                }
-                            });
-                        } else if (ds == DownloadEntry.DownloadedState.DOWNLOADING) {
-                            // may be download in progress
-                            EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.STARTED));
-                            viewHolder.bulkDownload.setVisibility(View.GONE);
-                            storage.getDownloadProgressByDmid(videoData.dmId, new DataCallback(true) {
-                                @Override
-                                public void onResult(Integer result) {
-                                    if (result >= 0 && result < 100) {
-                                        EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.STARTED));
-                                    } else if (result == 100) {
-                                        EventBus.getDefault().post(new DownloadEvent(DownloadEvent.DownloadStatus.COMPLETED));
-                                    }
-                                }
-
-                                @Override
-                                public void onFail(Exception ex) {
-                                    logger.error(ex);
-                                    viewHolder.bulkDownload.setVisibility(View.VISIBLE);
-                                }
-                            });
-                        } else if (ds == DownloadEntry.DownloadedState.DOWNLOADED) {
-                            // downloaded
-                            viewHolder.bulkDownload.setVisibility(View.GONE);
+                    public void onResult(DownloadEntry.WatchedState result) {
+                        if (result != null && result == DownloadEntry.WatchedState.WATCHED) {
+                            viewHolder.rowType.setIconColorResource(R.color.edx_grayscale_neutral_base);
+                        } else {
+                            viewHolder.rowType.setIconColorResource(R.color.edx_brand_primary_base);
                         }
-
                     }
 
                     @Override
                     public void onFail(Exception ex) {
                         logger.error(ex);
-                        viewHolder.bulkDownload.setVisibility(View.VISIBLE);
                     }
                 });
 
+        if (videoData.isVideoForWebOnly()) {
+            viewHolder.numOfVideoAndDownloadArea.setVisibility(View.GONE);
+        }
+        else {
+            viewHolder.numOfVideoAndDownloadArea.setVisibility(View.VISIBLE);
+            dbStore.getDownloadedStateForVideoId(videoData.videoId,
+                    new DataCallback(true) {
+                        @Override
+                        public void onResult(DownloadEntry.DownloadedState state) {
+                            if (state == null || state == DownloadEntry.DownloadedState.ONLINE) {
+                                // not yet downloaded
+                                setRowStateOnDownload(viewHolder, DownloadEntry.DownloadedState.ONLINE,
+                                        new View.OnClickListener() {
+                                            @Override
+                                            public void onClick(View v) {
+                                                mDownloadListener.download(videoData);
+                                            }
+                                        });
+                            } else if (state == DownloadEntry.DownloadedState.DOWNLOADING) {
+                                // may be download in progress
+                                setRowStateOnDownload(viewHolder, DownloadEntry.DownloadedState.DOWNLOADING,
+                                        new View.OnClickListener() {
+                                            @Override
+                                            public void onClick(View v) {
+                                                mDownloadListener.viewDownloadsStatus();
+                                            }
+                                        });
+                            } else if (state == DownloadEntry.DownloadedState.DOWNLOADED) {
+                                setRowStateOnDownload(viewHolder, DownloadEntry.DownloadedState.DOWNLOADED, null);
+                            }
+                        }
+
+                        @Override
+                        public void onFail(Exception ex) {
+                            logger.error(ex);
+                            viewHolder.bulkDownload.setVisibility(View.VISIBLE);
+                        }
+                    });
+        }
+
     }
 
     private  View getRowViewForContainer(int position, View convertView, ViewGroup parent, final SectionRow row) {
@@ -379,52 +354,75 @@ private  View getRowViewForContainer(int position, View convertView, ViewGroup p
             holder.rowSubtitle.setText(component.getFormat());
         }
 
-        //support video download for video type
-        final int totalCount = component.getBlockCount().videoCount;
-        if (totalCount == 0 ){
+        // support video download for video type excluding the ones only viewable on web
+        int webOnlyCount = dbStore.getWebOnlyVideosCountBySection(courseId, chapterId, sequentialId, null);
+        final int totalDownloadableVideos = component.getBlockCount().videoCount - webOnlyCount;
+        if (totalDownloadableVideos == 0 ){
             holder.numOfVideoAndDownloadArea.setVisibility(View.GONE);
         } else {
+            holder.bulkDownload.setVisibility(View.VISIBLE);
             holder.noOfVideos.setVisibility(View.VISIBLE);
-            holder.noOfVideos.setText("" + totalCount);
-
-            if (  row.numOfVideoNotDownloaded == 0 ){
-                holder.bulkDownload.setVisibility(View.GONE);
+            holder.noOfVideos.setText("" + totalDownloadableVideos);
+
+            Integer downloadedCount = dbStore.getDownloadedVideosCountForSection(courseId, chapterId, sequentialId, null);
+
+            if (downloadedCount == totalDownloadableVideos) {
+                holder.noOfVideos.setVisibility(View.VISIBLE);
+                setRowStateOnDownload(holder, DownloadEntry.DownloadedState.DOWNLOADED, null);
+            } else if (dbStore.getDownloadingVideosCountForSection(courseId, chapterId, sequentialId, null)
+                    + downloadedCount == totalDownloadableVideos) {
+                holder.noOfVideos.setVisibility(View.GONE);
+                setRowStateOnDownload(holder, DownloadEntry.DownloadedState.DOWNLOADING
+                    , new View.OnClickListener() {
+                            @Override
+                            public void onClick(View downloadView) {
+                                mDownloadListener.viewDownloadsStatus();
+                            }
+                        });
             } else {
-                int inProcessCount = dbStore.getVideosCountBySection(courseId, chapterId, sequentialId, null);
-                int webOnlyCount = dbStore.getWebOnlyVideosCountBySection(courseId, chapterId, sequentialId, null);
-                row.numOfVideoNotDownloaded = totalCount - inProcessCount - webOnlyCount;
-                if (row.numOfVideoNotDownloaded > 0) {
-                    holder.bulkDownload.setVisibility(View.VISIBLE);
-                    holder.numOfVideoAndDownloadArea
-                        .setOnClickListener(new View.OnClickListener() {
+                holder.noOfVideos.setVisibility(View.VISIBLE);
+                setRowStateOnDownload(holder, DownloadEntry.DownloadedState.ONLINE
+                    , new View.OnClickListener() {
                             @Override
                             public void onClick(View downloadView) {
                                 mDownloadListener.download(component.getVideos());
                             }
                         });
-                } else {
-                    holder.bulkDownload.setVisibility(View.GONE);
-                }
-            }
-        }
-
-        if (AppConstants.offline_flag) {
-            holder.numOfVideoAndDownloadArea.setVisibility(View.GONE);
-            boolean isVideoDownloaded = dbStore.isVideoDownloadedInSection
-                (courseId, chapterId, sequentialId, null);
-            if(isVideoDownloaded)
-            {
-                //TODO - any UI update
-            }else{
-                //TODO - any UI update
             }
-        } else {
-           //TODO - any UI update?
         }
 
         return convertView;
     }
 
+    /**
+     * Makes various changes to the row based on a video element's download status
+     *
+     * @param row      ViewHolder of the row view
+     * @param state    current state of video download
+     * @param listener the listener to attach to the video download button
+     */
+    private void setRowStateOnDownload(ViewHolder row, DownloadEntry.DownloadedState state
+            , View.OnClickListener listener) {
+        switch (state) {
+            case DOWNLOADING:
+                row.bulkDownload.setIcon(Iconify.IconValue.fa_spinner);
+                row.bulkDownload.setRotating(true);
+                row.bulkDownload.setIconColorResource(R.color.edx_brand_primary_base);
+                break;
+            case DOWNLOADED:
+                row.bulkDownload.setIcon(Iconify.IconValue.fa_check);
+                row.bulkDownload.setRotating(false);
+                row.bulkDownload.setIconColorResource(R.color.edx_grayscale_neutral_base);
+                break;
+            case ONLINE:
+                row.bulkDownload.setIcon(Iconify.IconValue.fa_arrow_down);
+                row.bulkDownload.setRotating(false);
+                row.bulkDownload.setIconColorResource(R.color.edx_grayscale_neutral_base);
+                break;
+        }
+        row.numOfVideoAndDownloadArea.setOnClickListener(listener);
+    }
+
     public  View getHeaderView(int position, View convertView, ViewGroup parent){
         final SectionRow row = this.getItem(position);
         TextView titleView = (TextView)convertView.findViewById(R.id.row_header);
@@ -470,13 +468,12 @@ public ViewHolder getTag(View convertView) {
                 .findViewById(R.id.row_subtitle);
         holder.rowSubtitleIcon = (IconView) convertView
                 .findViewById(R.id.row_subtitle_icon);
-        holder.rowSubtitleIcon.setIconColor(context.getResources().getColor(R.color.edx_grayscale_neutral_light));
+        holder.rowSubtitleIcon.setIconColorResource(R.color.edx_grayscale_neutral_light);
         holder.noOfVideos = (TextView) convertView
                 .findViewById(R.id.no_of_videos);
         holder.bulkDownload = (IconView) convertView
                 .findViewById(R.id.bulk_download);
-        holder.bulkDownload.setIconColor(context.getResources().getColor(
-                R.color.edx_grayscale_neutral_base));
+        holder.bulkDownload.setIconColorResource(R.color.edx_grayscale_neutral_base);
         holder.numOfVideoAndDownloadArea = (LinearLayout) convertView
                 .findViewById(R.id.bulk_download_layout);
         holder.rowSubtitlePanel =convertView.findViewById(R.id.row_subtitle_panel);
@@ -510,9 +507,6 @@ public static class SectionRow {
         public final boolean topComponent;
         public final CourseComponent component;
 
-        //field to cache the temp value
-        public int numOfVideoNotDownloaded = -1;
-
         public SectionRow(int type, CourseComponent component) {
             this(type, false, component);
         }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionCommentsAdapter.java b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionCommentsAdapter.java
index 67d17f529e..aa9df22e9c 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionCommentsAdapter.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionCommentsAdapter.java
@@ -57,7 +57,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
             final int childrenSize = discussionComment.getChildren().size();
             holder.discussionCommentCountReportTextView.setText(context.getResources().
                     getQuantityString(R.plurals.number_responses_or_comments_comments_label, childrenSize, childrenSize));
-            iconDrawable = new IconDrawable(context, Iconify.IconValue.fa_comment).sizeRes(R.dimen.edx_xxx_small).colorRes(R.color.edx_grayscale_neutral_base);
+            iconDrawable = new IconDrawable(context, Iconify.IconValue.fa_comment)
+                    .sizeRes(context, R.dimen.edx_xxx_small)
+                    .colorRes(context, R.color.edx_grayscale_neutral_base);
             holder.discussionCommentCountReportTextView.setOnClickListener(null);
             holder.discussionCommentCountReportTextView.setClickable(false);
 
@@ -74,8 +76,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
             }
 
             iconDrawable = new IconDrawable(context, Iconify.IconValue.fa_flag)
-                    .sizeRes(R.dimen.edx_xxx_small)
-                    .colorRes(discussionComment.isAbuseFlagged() ? R.color.edx_brand_primary_base : R.color.edx_grayscale_neutral_dark);
+                    .sizeRes(context, R.dimen.edx_xxx_small)
+                    .colorRes(context, discussionComment.isAbuseFlagged() ? R.color.edx_brand_primary_base : R.color.edx_grayscale_neutral_dark);
             holder.discussionCommentCountReportTextView.setText(discussionComment.isAbuseFlagged() ? context.getString(R.string.discussion_responses_reported_label) : context.getString(R.string.discussion_responses_report_label));
             holder.discussionCommentCountReportTextView.setTextColor(context.getResources().getColor(R.color.edx_grayscale_neutral_dark));
 
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionPostsSpinnerAdapter.java b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionPostsSpinnerAdapter.java
index 7c32086820..bc72aa49ba 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionPostsSpinnerAdapter.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionPostsSpinnerAdapter.java
@@ -46,8 +46,8 @@ private static class IconDrawableFactoryImpl implements IconDrawableFactory {
         @NonNull
         public IconDrawable createIcon() {
             return new IconDrawable(context, iconValue)
-                    .sizeRes(R.dimen.icon_view_standard_width_height)
-                    .colorRes(R.color.edx_brand_primary_base);
+                    .sizeRes(context, R.dimen.icon_view_standard_width_height)
+                    .colorRes(context, R.color.edx_brand_primary_base);
         }
     }
 
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionReportViewHolder.java b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionReportViewHolder.java
index 1eb6c2420f..df707eedbc 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionReportViewHolder.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/adapters/DiscussionReportViewHolder.java
@@ -29,6 +29,6 @@ public void setReported(boolean isReported) {
         reportTextView.setText(reportTextView.getResources().getString(reportStringResId));
 
         int iconColor = isReported ? R.color.edx_brand_primary_base : R.color.edx_grayscale_neutral_base;
-        reportIconView.setIconColor(reportIconView.getResources().getColor(iconColor));
+        reportIconView.setIconColorResource(iconColor);
     }
 }
diff --git a/VideoLocker/src/main/java/org/edx/mobile/view/view_holders/DiscussionSocialLayoutViewHolder.java b/VideoLocker/src/main/java/org/edx/mobile/view/view_holders/DiscussionSocialLayoutViewHolder.java
index 0c85352e13..2262f15ca4 100644
--- a/VideoLocker/src/main/java/org/edx/mobile/view/view_holders/DiscussionSocialLayoutViewHolder.java
+++ b/VideoLocker/src/main/java/org/edx/mobile/view/view_holders/DiscussionSocialLayoutViewHolder.java
@@ -42,26 +42,24 @@ public DiscussionSocialLayoutViewHolder(View itemView) {
     public void setDiscussionThread(final DiscussionThread discussionThread) {
         threadVoteTextView.setText(ResourceUtil.getFormattedStringForQuantity(
                 threadVoteTextView.getResources(), R.plurals.discussion_responses_action_bar_vote_text, discussionThread.getVoteCount()));
-        threadVoteIconView.setIconColor(discussionThread.isVoted() ?
-                threadVoteIconView.getResources().getColor(R.color.edx_brand_primary_base) :
-                threadVoteIconView.getResources().getColor(R.color.edx_grayscale_neutral_base));
+        threadVoteIconView.setIconColorResource(discussionThread.isVoted() ?
+                R.color.edx_brand_primary_base : R.color.edx_grayscale_neutral_base);
 
         threadFollowContainer.setVisibility(View.VISIBLE);
 
         if (discussionThread.isFollowing()) {
             threadFollowTextView.setText(R.string.forum_unfollow);
-            threadFollowIconView.setIconColor(threadFollowIconView.getResources().getColor(R.color.edx_brand_primary_base));
+            threadFollowIconView.setIconColorResource(R.color.edx_brand_primary_base);
         } else {
             threadFollowTextView.setText(R.string.forum_follow);
-            threadFollowIconView.setIconColor(threadFollowIconView.getResources().getColor(R.color.edx_grayscale_neutral_base));
+            threadFollowIconView.setIconColorResource(R.color.edx_grayscale_neutral_base);
         }
     }
 
     public void setDiscussionResponse(final DiscussionComment discussionResponse) {
         threadVoteTextView.setText(ResourceUtil.getFormattedStringForQuantity(
                 threadVoteTextView.getResources(), R.plurals.discussion_responses_action_bar_vote_text, discussionResponse.getVoteCount()));
-        threadVoteIconView.setIconColor(discussionResponse.isVoted() ?
-                threadVoteIconView.getResources().getColor(R.color.edx_brand_primary_base) :
-                threadVoteIconView.getResources().getColor(R.color.edx_grayscale_neutral_base));
+        threadVoteIconView.setIconColorResource(discussionResponse.isVoted() ?
+                R.color.edx_brand_primary_base : R.color.edx_grayscale_neutral_base);
     }
 }
diff --git a/VideoLocker/src/test/java/org/edx/mobile/view/CourseBaseActivityTest.java b/VideoLocker/src/test/java/org/edx/mobile/view/CourseBaseActivityTest.java
index 2525b5ad90..0d0b6da45f 100644
--- a/VideoLocker/src/test/java/org/edx/mobile/view/CourseBaseActivityTest.java
+++ b/VideoLocker/src/test/java/org/edx/mobile/view/CourseBaseActivityTest.java
@@ -8,11 +8,9 @@
 import android.view.Gravity;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
 import android.widget.ProgressBar;
 
 import org.edx.mobile.R;
-import org.edx.mobile.event.DownloadEvent;
 import org.edx.mobile.http.OkHttpUtil;
 import org.edx.mobile.model.api.EnrolledCoursesResponse;
 import org.edx.mobile.model.course.CourseComponent;
@@ -25,11 +23,8 @@
 import org.robolectric.shadows.ShadowActivity;
 import org.robolectric.util.ActivityController;
 
-import de.greenrobot.event.EventBus;
-
 import static org.assertj.android.api.Assertions.assertThat;
 import static org.junit.Assert.*;
-import static org.junit.Assume.*;
 
 public abstract class CourseBaseActivityTest extends BaseFragmentActivityTest {
     /**
@@ -100,10 +95,6 @@ public void initializeTest() {
         controller.create();
         assertNotNull(activity.findViewById(R.id.offline_bar));
         assertNotNull(activity.findViewById(R.id.last_access_bar));
-        assertNotNull(activity.findViewById(R.id.video_download_indicator));
-        View downloadInProgressButton =
-                activity.findViewById(R.id.download_in_progress_button);
-        assertNotNull(downloadInProgressButton);
         DrawerLayout drawerLayout = (DrawerLayout)
                 activity.findViewById(R.id.drawer_layout);
         if (drawerLayout != null) {
@@ -114,43 +105,6 @@ public void initializeTest() {
         }
 
         controller.postCreate(null).resume().postResume().visible();
-        // Is there any way to test options menu invalidation?
-        assertTrue(downloadInProgressButton.performClick());
-        assertNextStartedActivity(activity, DownloadListActivity.class);
-    }
-
-    /**
-     * Testing functionality upon receiving a DownloadEvent
-     */
-    @Test
-    @Ignore // Hangs indefinitely. Fixed in https://github.com/robolectric/robolectric/pull/2017
-    public void downloadEventTest() {
-        CourseBaseActivity activity =
-                Robolectric.buildActivity(getActivityClass())
-                        .withIntent(getIntent()).setup().get();
-        View downloadProgressBar =
-                activity.findViewById(R.id.download_in_progress_bar);
-        assumeNotNull(downloadProgressBar);
-        assertThat(downloadProgressBar).isNotVisible();
-        EventBus eventBus = EventBus.getDefault();
-        eventBus.post(new DownloadEvent(DownloadEvent.DownloadStatus.STARTED));
-        assertDownloadProgressBarVisible(activity.getWindow().getDecorView(),
-                downloadProgressBar);
-    }
-
-    /**
-     * Method for asserting the functionality of
-     * {@link CourseBaseActivity#setVisibilityForDownloadProgressView} which
-     * is called upon receiving a {@link DownloadEvent}. The base
-     * implementation just makes the download progress bar visible
-     * immediately, but this behaviour may be overridden in subclasses.
-     *
-     * @param rootView The root View for finding Views by id
-     * @param downloadProgressBar The download progress bar View
-     */
-    protected void assertDownloadProgressBarVisible(
-            View rootView, View downloadProgressBar) {
-        assertThat(downloadProgressBar).isVisible();
     }
 
     /**
diff --git a/VideoLocker/src/test/java/org/edx/mobile/view/CourseOutlineActivityTest.java b/VideoLocker/src/test/java/org/edx/mobile/view/CourseOutlineActivityTest.java
index 8cfd866728..ce13bbf02a 100644
--- a/VideoLocker/src/test/java/org/edx/mobile/view/CourseOutlineActivityTest.java
+++ b/VideoLocker/src/test/java/org/edx/mobile/view/CourseOutlineActivityTest.java
@@ -5,12 +5,9 @@
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 import android.widget.ListView;
 
 import org.edx.mobile.R;
-import org.edx.mobile.event.DownloadEvent;
 import org.edx.mobile.http.OkHttpUtil;
 import org.edx.mobile.model.api.EnrolledCoursesResponse;
 import org.edx.mobile.model.course.BlockPath;
@@ -18,15 +15,12 @@
 import org.edx.mobile.model.course.CourseComponent;
 import org.edx.mobile.model.course.IBlock;
 import org.edx.mobile.module.prefs.PrefManager;
-import org.edx.mobile.third_party.iconify.IconView;
 import org.junit.Test;
 import org.junit.runners.Parameterized.Parameters;
 import org.robolectric.Robolectric;
 import org.robolectric.Shadows;
 import org.robolectric.shadows.ShadowActivity;
-import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.util.ActivityController;
-import org.robolectric.util.Scheduler;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -295,40 +289,4 @@ private void clickRow(ActivityController contro
         listView.performItemClick(listView.getChildAt(rowIndex),
                 rowIndex, listView.getItemIdAtPosition(rowIndex));
     }
-
-    /**
-     * Method for asserting the functionality of
-     * {@link CourseBaseActivity#setVisibilityForDownloadProgressView} which
-     * is called upon receiving a {@link DownloadEvent}. This overrides the
-     * base implementation, as {@link CourseVideoListActivity} performs a
-     * delayed callback with a more detailed implementation.
-     *
-     * @param rootView The root View for finding Views by id
-     * @param downloadProgressBar The download progress bar View
-     */
-    @Override
-    protected void assertDownloadProgressBarVisible(
-            View rootView, View downloadProgressBar) {
-        View downloadIndicatorView = rootView.findViewById(
-                R.id.video_download_indicator);
-        assertNotNull(downloadIndicatorView);
-        assertThat(downloadIndicatorView).isInstanceOf(IconView.class);
-        boolean indicatorHadAnimation =
-                downloadIndicatorView.getAnimation() != null;
-
-        // The animation is started from a delayed callback, so the
-        // foreground scheduler needs to be advanced to it.
-        Scheduler foregroundScheduler = ShadowApplication.getInstance()
-                .getForegroundThreadScheduler();
-        foregroundScheduler.advanceToNextPostedRunnable();
-        assertThat(downloadProgressBar).isVisible();
-        if (!indicatorHadAnimation) {
-            assertThat(downloadIndicatorView).isVisible();
-            Animation animation = downloadIndicatorView.getAnimation();
-            assertNotNull(animation);
-            assertThat(animation.getStartTime())
-                    .isLessThanOrEqualTo(AnimationUtils.currentAnimationTimeMillis());
-            assertThat(animation).hasStartOffset(0);
-        }
-    }
 }
diff --git a/VideoLocker/src/test/java/org/edx/mobile/view/CourseUnitNavigationActivityTest.java b/VideoLocker/src/test/java/org/edx/mobile/view/CourseUnitNavigationActivityTest.java
index f491af18f2..3ee5e39cf1 100644
--- a/VideoLocker/src/test/java/org/edx/mobile/view/CourseUnitNavigationActivityTest.java
+++ b/VideoLocker/src/test/java/org/edx/mobile/view/CourseUnitNavigationActivityTest.java
@@ -20,7 +20,6 @@
 import org.edx.mobile.model.course.VideoBlockModel;
 import org.edx.mobile.module.prefs.PrefManager;
 import org.edx.mobile.view.custom.DisableableViewPager;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.robolectric.Robolectric;
 import org.robolectric.RuntimeEnvironment;
@@ -368,13 +367,4 @@ public void orientationChangeTest() {
                 activity.getResources().getConfiguration().orientation);
         assertOrientationSetup(activity);
     }
-
-    /**
-     * Ignoring functionality testing upon receiving a DownloadEvent, as
-     * {@link CourseUnitNavigationActivity} also overrides and ignores it
-     */
-    @Override
-    @Ignore
-    @Test
-    public void downloadEventTest() {}
 }