/** * This method should be called in the prepare-methods of menus. It changes the visibility of the * menu items depending on a FeedItem's attributes. * * @param mi An instance of MenuInterface that the method uses to change a MenuItem's visibility * @param selectedItem The FeedItem for which the menu is supposed to be prepared * @param showExtendedMenu True if MenuItems that let the user share information about the * FeedItem and visit its website should be set visible. This parameter should be set to false * if the menu space is limited. * @param queueAccess Used for testing if the queue contains the selected item * @return Returns true if selectedItem is not null. */ public static boolean onPrepareMenu( MenuInterface mi, FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess) { if (selectedItem == null) { return false; } DownloadRequester requester = DownloadRequester.getInstance(); boolean hasMedia = selectedItem.getMedia() != null; boolean downloaded = hasMedia && selectedItem.getMedia().isDownloaded(); boolean downloading = hasMedia && requester.isDownloadingFile(selectedItem.getMedia()); boolean notLoadedAndNotLoading = hasMedia && (!downloaded) && (!downloading); boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING; FeedItem.State state = selectedItem.getState(); if (!isPlaying) { mi.setItemVisibility(R.id.skip_episode_item, false); } if (!downloaded || isPlaying) { mi.setItemVisibility(R.id.play_item, false); mi.setItemVisibility(R.id.remove_item, false); } if (!notLoadedAndNotLoading) { mi.setItemVisibility(R.id.download_item, false); } if (!(notLoadedAndNotLoading | downloading) | isPlaying) { mi.setItemVisibility(R.id.stream_item, false); } if (!downloading) { mi.setItemVisibility(R.id.cancel_download_item, false); } boolean isInQueue = queueAccess.contains(selectedItem.getId()); if (!isInQueue || isPlaying) { mi.setItemVisibility(R.id.remove_from_queue_item, false); } if (!(!isInQueue && selectedItem.getMedia() != null)) { mi.setItemVisibility(R.id.add_to_queue_item, false); } if (!showExtendedMenu || selectedItem.getLink() == null) { mi.setItemVisibility(R.id.share_link_item, false); } if (!AppConfig.DEBUG || !(state == FeedItem.State.IN_PROGRESS || state == FeedItem.State.READ)) { mi.setItemVisibility(R.id.mark_unread_item, false); } if (!(state == FeedItem.State.NEW || state == FeedItem.State.IN_PROGRESS)) { mi.setItemVisibility(R.id.mark_read_item, false); } if (!showExtendedMenu || selectedItem.getLink() == null) { mi.setItemVisibility(R.id.visit_website_item, false); } if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) { mi.setItemVisibility(R.id.support_item, false); } return true; }
/** * Waits for completed requests. Once the first request has been taken, the method will wait * WAIT_TIMEOUT ms longer to collect more completed requests. * * @return Collected feeds or null if the method has been interrupted during the first waiting * period. */ private List<Feed> collectCompletedRequests() { List<Feed> results = new LinkedList<Feed>(); DownloadRequester requester = DownloadRequester.getInstance(); int tasks = 0; try { DownloadRequest request = completedRequests.take(); parserService.submit(new FeedParserTask(request)); tasks++; } catch (InterruptedException e) { return null; } tasks += pollCompletedDownloads(); isCollectingRequests = true; if (requester.isDownloadingFeeds()) { // wait for completion of more downloads long startTime = System.currentTimeMillis(); long currentTime = startTime; while (requester.isDownloadingFeeds() && (currentTime - startTime) < WAIT_TIMEOUT) { try { if (BuildConfig.DEBUG) Log.d(TAG, "Waiting for " + (startTime + WAIT_TIMEOUT - currentTime) + " ms"); sleep(startTime + WAIT_TIMEOUT - currentTime); } catch (InterruptedException e) { if (BuildConfig.DEBUG) Log.d(TAG, "interrupted while waiting for more downloads"); tasks += pollCompletedDownloads(); } finally { currentTime = System.currentTimeMillis(); } } tasks += pollCompletedDownloads(); } isCollectingRequests = false; for (int i = 0; i < tasks; i++) { try { Feed f = parserService.take().get(); if (f != null) { results.add(f); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } return results; }
/** * Updates the contents of the service's notifications. Should be called before * setupNotificationBuilders. */ @SuppressLint("NewApi") private Notification updateNotifications() { String contentTitle = getString(R.string.download_notification_title); int numDownloads = requester.getNumberOfDownloads(); String downloadsLeft; if (numDownloads > 0) { downloadsLeft = requester.getNumberOfDownloads() + getString(R.string.downloads_left); } else { downloadsLeft = getString(R.string.downloads_processing); } if (android.os.Build.VERSION.SDK_INT >= 16) { if (notificationBuilder != null) { StringBuilder bigText = new StringBuilder(""); for (int i = 0; i < downloads.size(); i++) { Downloader downloader = downloads.get(i); final DownloadRequest request = downloader.getDownloadRequest(); if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { if (request.getTitle() != null) { if (i > 0) { bigText.append("\n"); } bigText.append("\u2022 " + request.getTitle()); } } else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { if (request.getTitle() != null) { if (i > 0) { bigText.append("\n"); } bigText.append( "\u2022 " + request.getTitle() + " (" + request.getProgressPercent() + "%)"); } } } notificationBuilder.setSummaryText(downloadsLeft); notificationBuilder.setBigContentTitle(contentTitle); if (bigText != null) { notificationBuilder.bigText(bigText.toString()); } return notificationBuilder.build(); } } else { if (notificationCompatBuilder != null) { notificationCompatBuilder.setContentTitle(contentTitle); notificationCompatBuilder.setContentText(downloadsLeft); return notificationCompatBuilder.build(); } } return null; }
public static boolean onMenuItemClicked(Context context, int menuItemId, FeedItem selectedItem) throws DownloadRequestException { DownloadRequester requester = DownloadRequester.getInstance(); switch (menuItemId) { case R.id.skip_episode_item: context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); break; case R.id.download_item: DBTasks.downloadFeedItems(context, selectedItem); break; case R.id.play_item: DBTasks.playMedia(context, selectedItem.getMedia(), true, true, false); break; case R.id.remove_item: DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId()); break; case R.id.cancel_download_item: requester.cancelDownload(context, selectedItem.getMedia()); break; case R.id.mark_read_item: DBWriter.markItemRead(context, selectedItem, true, true); break; case R.id.mark_unread_item: DBWriter.markItemRead(context, selectedItem, false, true); break; case R.id.add_to_queue_item: DBWriter.addQueueItem(context, selectedItem.getId()); break; case R.id.remove_from_queue_item: DBWriter.removeQueueItem(context, selectedItem.getId(), true); break; case R.id.stream_item: DBTasks.playMedia(context, selectedItem.getMedia(), true, true, true); break; case R.id.visit_website_item: Uri uri = Uri.parse(selectedItem.getLink()); context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); break; case R.id.support_item: DBTasks.flattrItemIfLoggedIn(context, selectedItem); break; case R.id.share_link_item: ShareUtils.shareFeedItemLink(context, selectedItem); break; default: return false; } // Refresh menu state return true; }
public static boolean onMenuItemClicked(Context context, MenuItem item, FeedItem selectedItem) throws DownloadRequestException { DownloadRequester requester = DownloadRequester.getInstance(); FeedManager manager = FeedManager.getInstance(); switch (item.getItemId()) { case R.id.download_item: manager.downloadFeedItem(context, selectedItem); break; case R.id.play_item: manager.playMedia(context, selectedItem.getMedia(), true, true, false); break; case R.id.remove_item: manager.deleteFeedMedia(context, selectedItem.getMedia()); break; case R.id.cancel_download_item: requester.cancelDownload(context, selectedItem.getMedia()); break; case R.id.mark_read_item: manager.markItemRead(context, selectedItem, true, true); break; case R.id.mark_unread_item: manager.markItemRead(context, selectedItem, false, true); break; case R.id.add_to_queue_item: manager.addQueueItem(context, selectedItem); break; case R.id.remove_from_queue_item: manager.removeQueueItem(context, selectedItem); break; case R.id.stream_item: manager.playMedia(context, selectedItem.getMedia(), true, true, true); break; case R.id.visit_website_item: Uri uri = Uri.parse(selectedItem.getLink()); context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); break; case R.id.support_item: new FlattrClickWorker(context, selectedItem.getPaymentLink()).executeAsync(); break; case R.id.share_link_item: ShareUtils.shareFeedItemLink(context, selectedItem); break; default: return false; } // Refresh menu state return true; }
@Override public void run() { if (BuildConfig.DEBUG) Log.d(TAG, "downloadCompletionThread was started"); while (!isInterrupted()) { try { Downloader downloader = downloadExecutor.take().get(); if (BuildConfig.DEBUG) Log.d(TAG, "Received 'Download Complete' - message."); removeDownload(downloader); DownloadStatus status = downloader.getResult(); boolean successful = status.isSuccessful(); final int type = status.getFeedfileType(); if (successful) { if (type == Feed.FEEDFILETYPE_FEED) { handleCompletedFeedDownload(downloader.getDownloadRequest()); } else if (type == FeedImage.FEEDFILETYPE_FEEDIMAGE) { handleCompletedImageDownload(status, downloader.getDownloadRequest()); } else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { handleCompletedFeedMediaDownload(status, downloader.getDownloadRequest()); } } else { numberOfDownloads.decrementAndGet(); if (!status.isCancelled()) { if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) { postAuthenticationNotification(downloader.getDownloadRequest()); } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR && Integer.valueOf(status.getReasonDetailed()) == HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE) { Log.d(TAG, "Requested invalid range, restarting download from the beginning"); FileUtils.deleteQuietly( new File(downloader.getDownloadRequest().getDestination())); DownloadRequester.getInstance() .download(DownloadService.this, downloader.getDownloadRequest()); } else { Log.e(TAG, "Download failed"); saveDownloadStatus(status); handleFailedDownload(status, downloader.getDownloadRequest()); } } sendDownloadHandledIntent(); queryDownloadsAsync(); } } catch (InterruptedException e) { if (BuildConfig.DEBUG) Log.d(TAG, "DownloadCompletionThread was interrupted"); } catch (ExecutionException e) { e.printStackTrace(); numberOfDownloads.decrementAndGet(); } } if (BuildConfig.DEBUG) Log.d(TAG, "End of downloadCompletionThread"); }
/** Check if there's something else to download, otherwise stop */ void queryDownloads() { if (AppConfig.DEBUG) { Log.d(TAG, numberOfDownloads.get() + " downloads left"); } if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) { if (AppConfig.DEBUG) Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown"); stopSelf(); } else { setupNotificationUpdater(); startForeground(NOTIFICATION_ID, updateNotifications()); } }
public static boolean onPrepareMenu(Menu menu, FeedItem selectedItem) { FeedManager manager = FeedManager.getInstance(); DownloadRequester requester = DownloadRequester.getInstance(); boolean hasMedia = selectedItem.getMedia() != null; boolean downloaded = hasMedia && selectedItem.getMedia().isDownloaded(); boolean downloading = hasMedia && requester.isDownloadingFile(selectedItem.getMedia()); boolean notLoadedAndNotLoading = hasMedia && (!downloaded) && (!downloading); FeedItem.State state = selectedItem.getState(); menu.findItem(R.id.play_item).setVisible(downloaded); menu.findItem(R.id.remove_item).setVisible(downloaded); menu.findItem(R.id.download_item).setVisible(notLoadedAndNotLoading); menu.findItem(R.id.stream_item).setVisible(notLoadedAndNotLoading | downloading); menu.findItem(R.id.cancel_download_item).setVisible(downloading); boolean isInQueue = manager.isInQueue(selectedItem); menu.findItem(R.id.remove_from_queue_item).setVisible(isInQueue); menu.findItem(R.id.add_to_queue_item).setVisible(!isInQueue && selectedItem.getMedia() != null); menu.findItem(R.id.share_link_item).setVisible(selectedItem.getLink() != null); menu.findItem(R.id.mark_unread_item) .setVisible(state == FeedItem.State.IN_PROGRESS || state == FeedItem.State.READ); menu.findItem(R.id.mark_read_item) .setVisible(state == FeedItem.State.NEW || state == FeedItem.State.IN_PROGRESS); if (selectedItem.getLink() != null) { menu.findItem(R.id.visit_website_item).setVisible(true); } if (selectedItem.getPaymentLink() != null) { menu.findItem(R.id.support_item).setVisible(true); } return true; }
@Override protected Void doInBackground(Void... params) { File f = null; if (image.getFile_url() != null) { f = new File(image.getFile_url()); } if (image.getFile_url() != null && f.exists()) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(image.getFile_url(), options); int sampleSize = calculateSampleSize(options.outWidth, options.outHeight); options.inJustDecodeBounds = false; options.inSampleSize = sampleSize; decodedBitmap = BitmapFactory.decodeFile(image.getFile_url(), options); if (decodedBitmap == null) { Log.i( TAG, "Bitmap could not be decoded in custom sample size. Trying default sample size (path was " + image.getFile_url() + ")"); decodedBitmap = BitmapFactory.decodeFile(image.getFile_url()); } if (decodedBitmap != null) { bitmap = Bitmap.createScaledBitmap(decodedBitmap, PREFERRED_LENGTH, PREFERRED_LENGTH, false); if (baseLength == LENGTH_BASE_COVER) { addBitmapToCoverCache(image.getId(), bitmap); } else if (baseLength == LENGTH_BASE_THUMBNAIL) { addBitmapToThumbnailCache(image.getId(), bitmap); } } else { Log.w(TAG, "Could not load bitmap. Using default image."); bitmap = BitmapFactory.decodeResource(target.getResources(), R.drawable.default_cover); } if (AppConfig.DEBUG) Log.d(TAG, "Finished loading bitmaps"); } else { Log.e(TAG, "FeedImage has no valid file url. Using default image"); bitmap = BitmapFactory.decodeResource(target.getResources(), R.drawable.default_cover); if (image.getFile_url() != null && !DownloadRequester.getInstance().isDownloadingFile(image)) { FeedManager.getInstance().notifyInvalidImageFile(PodcastApp.getInstance(), image); } } return null; }
@SuppressLint("NewApi") @Override public void onCreate() { if (AppConfig.DEBUG) Log.d(TAG, "Service started"); isRunning = true; handler = new Handler(); completedDownloads = Collections.synchronizedList(new ArrayList<DownloadStatus>()); downloads = new ArrayList<Downloader>(); numberOfDownloads = new AtomicInteger(0); IntentFilter cancelDownloadReceiverFilter = new IntentFilter(); cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_ALL_DOWNLOADS); cancelDownloadReceiverFilter.addAction(ACTION_CANCEL_DOWNLOAD); registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter); syncExecutor = Executors.newSingleThreadExecutor( new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setPriority(Thread.MIN_PRIORITY); return t; } }); downloadExecutor = new ExecutorCompletionService<Downloader>( Executors.newFixedThreadPool( NUM_PARALLEL_DOWNLOADS, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setPriority(Thread.MIN_PRIORITY); return t; } })); schedExecutor = new ScheduledThreadPoolExecutor( SCHED_EX_POOL_SIZE, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setPriority(Thread.MIN_PRIORITY); return t; } }, new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { Log.w(TAG, "SchedEx rejected submission of new task"); } }); downloadCompletionThread.start(); setupNotificationBuilders(); requester = DownloadRequester.getInstance(); }
@Override protected void onCreate(Bundle savedInstanceState) { setTheme(PodcastApp.getThemeResourceId()); super.onCreate(savedInstanceState); getSupportActionBar().setDisplayHomeAsUpEnabled(true); StorageUtils.checkStorageAvailability(this); setContentView(R.layout.addfeed); requester = DownloadRequester.getInstance(); progDialog = new ProgressDialog(this); etxtFeedurl = (EditText) findViewById(R.id.etxtFeedurl); butBrowseMiroGuide = (Button) findViewById(R.id.butBrowseMiroguide); butOpmlImport = (Button) findViewById(R.id.butOpmlImport); butGreaderImport = (Button) findViewById(R.id.butGreaderImport); butConfirm = (Button) findViewById(R.id.butConfirm); butCancel = (Button) findViewById(R.id.butCancel); butBrowseMiroGuide.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(AddFeedActivity.this, MiroGuideMainActivity.class)); } }); butOpmlImport.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(AddFeedActivity.this, OpmlImportFromPathActivity.class)); } }); butGreaderImport.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(AddFeedActivity.this, GoogleReaderImportActivity.class)); } }); butConfirm.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { addNewFeed(); } }); butCancel.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setResult(RESULT_CANCELED); finish(); } }); }