diff options
author | ByteHamster <ByteHamster@users.noreply.github.com> | 2024-03-31 18:40:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-31 18:40:15 +0200 |
commit | edb440a5a9a05e24c344a71b272b9238217e9c55 (patch) | |
tree | 13623ca7d0dac052ac35d693aac940d0727c87f9 /core/src/main | |
parent | 4e47691e70e85736c7eeb30ce02c73176e565a86 (diff) | |
download | AntennaPod-edb440a5a9a05e24c344a71b272b9238217e9c55.zip |
Restructure related UI classes together (#7044)
Diffstat (limited to 'core/src/main')
17 files changed, 20 insertions, 868 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilterGroup.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilterGroup.java deleted file mode 100644 index fbdf6b3a9..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilterGroup.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.danoeh.antennapod.core.feed; - -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.model.feed.FeedItemFilter; - -public enum FeedItemFilterGroup { - PLAYED(new ItemProperties(R.string.hide_played_episodes_label, FeedItemFilter.PLAYED), - new ItemProperties(R.string.not_played, FeedItemFilter.UNPLAYED)), - PAUSED(new ItemProperties(R.string.hide_paused_episodes_label, FeedItemFilter.PAUSED), - new ItemProperties(R.string.not_paused, FeedItemFilter.NOT_PAUSED)), - FAVORITE(new ItemProperties(R.string.hide_is_favorite_label, FeedItemFilter.IS_FAVORITE), - new ItemProperties(R.string.not_favorite, FeedItemFilter.NOT_FAVORITE)), - MEDIA(new ItemProperties(R.string.has_media, FeedItemFilter.HAS_MEDIA), - new ItemProperties(R.string.no_media, FeedItemFilter.NO_MEDIA)), - QUEUED(new ItemProperties(R.string.queued_label, FeedItemFilter.QUEUED), - new ItemProperties(R.string.not_queued_label, FeedItemFilter.NOT_QUEUED)), - DOWNLOADED(new ItemProperties(R.string.hide_downloaded_episodes_label, FeedItemFilter.DOWNLOADED), - new ItemProperties(R.string.hide_not_downloaded_episodes_label, FeedItemFilter.NOT_DOWNLOADED)); - - public final ItemProperties[] values; - - FeedItemFilterGroup(ItemProperties... values) { - this.values = values; - } - - public static class ItemProperties { - - public final int displayName; - public final String filterId; - - public ItemProperties(int displayName, String filterId) { - this.displayName = displayName; - this.filterId = filterId; - } - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java deleted file mode 100644 index cea5d96ef..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.danoeh.antennapod.core.feed; - -import de.danoeh.antennapod.core.R; - -public enum SubscriptionsFilterGroup { - COUNTER_GREATER_ZERO(new ItemProperties(R.string.subscriptions_counter_greater_zero, "counter_greater_zero")), - AUTO_DOWNLOAD(new ItemProperties(R.string.auto_downloaded, "enabled_auto_download"), - new ItemProperties(R.string.not_auto_downloaded, "disabled_auto_download")), - UPDATED(new ItemProperties(R.string.kept_updated, "enabled_updates"), - new ItemProperties(R.string.not_kept_updated, "disabled_updates")), - NEW_EPISODE_NOTIFICATION(new ItemProperties(R.string.new_episode_notification_enabled, - "episode_notification_enabled"), - new ItemProperties(R.string.new_episode_notification_disabled, "episode_notification_disabled")); - - - public final ItemProperties[] values; - - SubscriptionsFilterGroup(ItemProperties... values) { - this.values = values; - } - - public static class ItemProperties { - - public final int displayName; - public final String filterId; - - public ItemProperties(int displayName, String filterId) { - this.displayName = displayName; - this.filterId = filterId; - } - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java b/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java deleted file mode 100644 index 829add126..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.danoeh.antennapod.core.menuhandler; - -import android.view.Menu; -import android.view.MenuItem; - -/** - * Utilities for menu items - */ -public class MenuItemUtils { - - /** - * When pressing a context menu item, Android calls onContextItemSelected - * for ALL fragments in arbitrary order, not just for the fragment that the - * context menu was created from. This assigns the listener to every menu item, - * so that the correct fragment is always called first and can consume the click. - * <p /> - * Note that Android still calls the onContextItemSelected methods of all fragments - * when the passed listener returns false. - */ - public static void setOnClickListeners(Menu menu, MenuItem.OnMenuItemClickListener listener) { - for (int i = 0; i < menu.size(); i++) { - if (menu.getItem(i).getSubMenu() != null) { - setOnClickListeners(menu.getItem(i).getSubMenu(), listener); - } - menu.getItem(i).setOnMenuItemClickListener(listener); - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java index 93f7d578a..dbcc899ba 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java @@ -1,6 +1,9 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; import android.util.Log; import java.util.ArrayList; @@ -15,7 +18,6 @@ import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterfa import de.danoeh.antennapod.storage.database.DBReader; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.net.common.NetworkUtils; -import de.danoeh.antennapod.core.util.PowerUtils; /** * Implements the automatic download algorithm used by AntennaPod. This class assumes that @@ -42,8 +44,7 @@ public class AutomaticDownloadAlgorithm { && UserPreferences.isEnableAutodownload(); // true if we should auto download based on power status - boolean powerShouldAutoDl = PowerUtils.deviceCharging(context) - || UserPreferences.isEnableAutodownloadOnBattery(); + boolean powerShouldAutoDl = deviceCharging(context) || UserPreferences.isEnableAutodownloadOnBattery(); // we should only auto download if both network AND power are happy if (networkShouldAutoDl && powerShouldAutoDl) { @@ -103,4 +104,18 @@ public class AutomaticDownloadAlgorithm { } }; } + + /** + * @return true if the device is charging + */ + public static boolean deviceCharging(Context context) { + // from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + Intent batteryStatus = context.registerReceiver(null, intentFilter); + + int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + return (status == BatteryManager.BATTERY_STATUS_CHARGING + || status == BatteryManager.BATTERY_STATUS_FULL); + + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterMerger.java index 9bbab7251..794b2322d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterMerger.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.core.feed; +package de.danoeh.antennapod.core.util; import android.text.TextUtils; import android.util.Log; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java index 1af81802b..b32b05236 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java @@ -7,7 +7,6 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import de.danoeh.antennapod.model.feed.Chapter; -import de.danoeh.antennapod.core.feed.ChapterMerger; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.net.common.AntennapodHttpClient; import de.danoeh.antennapod.storage.database.DBReader; diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/util/ConfirmationDialog.java index b964c7508..ff5e56f5d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ConfirmationDialog.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.core.dialog; +package de.danoeh.antennapod.core.util; import android.content.Context; import android.content.DialogInterface; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadErrorLabel.java b/core/src/main/java/de/danoeh/antennapod/core/util/DownloadErrorLabel.java deleted file mode 100644 index 3d2558a9f..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadErrorLabel.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import androidx.annotation.StringRes; -import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.model.download.DownloadError; - -/** - * Provides user-visible labels for download errors. - */ -public class DownloadErrorLabel { - - @StringRes - public static int from(DownloadError error) { - switch (error) { - case SUCCESS: return R.string.download_successful; - case ERROR_PARSER_EXCEPTION: return R.string.download_error_parser_exception; - case ERROR_UNSUPPORTED_TYPE: return R.string.download_error_unsupported_type; - case ERROR_CONNECTION_ERROR: return R.string.download_error_connection_error; - case ERROR_MALFORMED_URL: return R.string.download_error_error_unknown; - case ERROR_IO_ERROR: return R.string.download_error_io_error; - case ERROR_FILE_EXISTS: return R.string.download_error_error_unknown; - case ERROR_DOWNLOAD_CANCELLED: return R.string.download_canceled_msg; - case ERROR_DEVICE_NOT_FOUND: return R.string.download_error_device_not_found; - case ERROR_HTTP_DATA_ERROR: return R.string.download_error_http_data_error; - case ERROR_NOT_ENOUGH_SPACE: return R.string.download_error_insufficient_space; - case ERROR_UNKNOWN_HOST: return R.string.download_error_unknown_host; - case ERROR_REQUEST_ERROR: return R.string.download_error_request_error; - case ERROR_DB_ACCESS_ERROR: return R.string.download_error_db_access; - case ERROR_UNAUTHORIZED: return R.string.download_error_unauthorized; - case ERROR_FILE_TYPE: return R.string.download_error_file_type_type; - case ERROR_FORBIDDEN: return R.string.download_error_forbidden; - case ERROR_IO_WRONG_SIZE: return R.string.download_error_wrong_size; - case ERROR_IO_BLOCKED: return R.string.download_error_blocked; - case ERROR_UNSUPPORTED_TYPE_HTML: return R.string.download_error_unsupported_type_html; - case ERROR_NOT_FOUND: return R.string.download_error_not_found; - case ERROR_CERTIFICATE: return R.string.download_error_certificate; - case ERROR_PARSER_EXCEPTION_DUPLICATE: return R.string.download_error_parser_exception; - default: - if (BuildConfig.DEBUG) { - throw new IllegalArgumentException("No mapping from download error to label"); - } - return R.string.download_error_error_unknown; - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java deleted file mode 100644 index a1fadb4dc..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; - -/** - * Created by Tom on 1/5/15. - */ -public class PowerUtils { - - private PowerUtils() { - - } - - /** - * @return true if the device is charging - */ - public static boolean deviceCharging(Context context) { - // from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html - IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - Intent batteryStatus = context.registerReceiver(null, iFilter); - - int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); - return (status == BatteryManager.BATTERY_STATUS_CHARGING || - status == BatteryManager.BATTERY_STATUS_FULL); - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java deleted file mode 100644 index 316771123..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java +++ /dev/null @@ -1,91 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.core.app.ShareCompat; -import androidx.core.content.FileProvider; - -import de.danoeh.antennapod.ui.common.Converter; -import java.io.File; -import java.net.URLEncoder; - -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; - -/** Utility methods for sharing data */ -public class ShareUtils { - private static final String TAG = "ShareUtils"; - - private ShareUtils() { - } - - public static void shareLink(@NonNull Context context, @NonNull String text) { - Intent intent = new ShareCompat.IntentBuilder(context) - .setType("text/plain") - .setText(text) - .setChooserTitle(R.string.share_url_label) - .createChooserIntent(); - context.startActivity(intent); - } - - public static void shareFeedLink(Context context, Feed feed) { - String text = feed.getTitle() - + "\n\n" - + "https://antennapod.org/deeplink/subscribe/?url=" - + URLEncoder.encode(feed.getDownloadUrl()) - + "&title=" - + URLEncoder.encode(feed.getTitle()); - shareLink(context, text); - } - - public static boolean hasLinkToShare(FeedItem item) { - return FeedItemUtil.getLinkWithFallback(item) != null; - } - - public static void shareMediaDownloadLink(Context context, FeedMedia media) { - shareLink(context, media.getDownloadUrl()); - } - - public static void shareFeedItemLinkWithDownloadLink(Context context, FeedItem item, boolean withPosition) { - String text = item.getFeed().getTitle() + ": " + item.getTitle(); - int pos = 0; - if (item.getMedia() != null && withPosition) { - text += "\n" + context.getResources().getString(R.string.share_starting_position_label) + ": "; - pos = item.getMedia().getPosition(); - text += Converter.getDurationStringLong(pos); - } - - if (hasLinkToShare(item)) { - text += "\n\n" + context.getResources().getString(R.string.share_dialog_episode_website_label) + ": "; - text += FeedItemUtil.getLinkWithFallback(item); - } - - if (item.getMedia() != null && item.getMedia().getDownloadUrl() != null) { - text += "\n\n" + context.getResources().getString(R.string.share_dialog_media_file_label) + ": "; - text += item.getMedia().getDownloadUrl(); - if (withPosition) { - text += "#t=" + pos / 1000; - } - } - shareLink(context, text); - } - - public static void shareFeedItemFile(Context context, FeedMedia media) { - Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority), - new File(media.getLocalFileUrl())); - - new ShareCompat.IntentBuilder(context) - .setType(media.getMimeType()) - .addStream(fileUri) - .setChooserTitle(R.string.share_file_label) - .startChooser(); - - Log.e(TAG, "shareFeedItemFile called"); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java deleted file mode 100644 index 779f3b947..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/download/MediaSizeLoader.java +++ /dev/null @@ -1,78 +0,0 @@ -package de.danoeh.antennapod.core.util.download; - -import android.text.TextUtils; -import de.danoeh.antennapod.net.common.AntennapodHttpClient; -import de.danoeh.antennapod.storage.database.DBWriter; -import de.danoeh.antennapod.net.common.NetworkUtils; -import de.danoeh.antennapod.model.feed.FeedMedia; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import android.util.Log; -import okhttp3.Response; - -import java.io.File; -import java.io.IOException; - -public abstract class MediaSizeLoader { - private static final String TAG = "MediaSizeLoader"; - - public static Single<Long> getFeedMediaSizeObservable(FeedMedia media) { - return Single.create((SingleOnSubscribe<Long>) emitter -> { - if (!NetworkUtils.isEpisodeHeadDownloadAllowed()) { - emitter.onSuccess(0L); - return; - } - long size = Integer.MIN_VALUE; - if (media.isDownloaded()) { - File mediaFile = new File(media.getLocalFileUrl()); - if (mediaFile.exists()) { - size = mediaFile.length(); - } - } else if (!media.checkedOnSizeButUnknown()) { - // only query the network if we haven't already checked - - String url = media.getDownloadUrl(); - if (TextUtils.isEmpty(url)) { - emitter.onSuccess(0L); - return; - } - - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .url(url) - .header("Accept-Encoding", "identity") - .head(); - try { - Response response = client.newCall(httpReq.build()).execute(); - if (response.isSuccessful()) { - String contentLength = response.header("Content-Length"); - try { - size = Integer.parseInt(contentLength); - } catch (NumberFormatException e) { - Log.e(TAG, Log.getStackTraceString(e)); - } - } - } catch (IOException e) { - emitter.onSuccess(0L); - Log.e(TAG, Log.getStackTraceString(e)); - return; // better luck next time - } - } - Log.d(TAG, "new size: " + size); - if (size <= 0) { - // they didn't tell us the size, but we don't want to keep querying on it - media.setCheckedOnSizeButUnknown(); - } else { - media.setSize(size); - } - emitter.onSuccess(size); - DBWriter.setFeedMedia(media); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java deleted file mode 100644 index 79c6e76e1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/download/NetworkConnectionChangeHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.danoeh.antennapod.core.util.download; - -import android.content.Context; -import android.util.Log; -import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; -import de.danoeh.antennapod.net.common.NetworkUtils; - -public abstract class NetworkConnectionChangeHandler { - private static final String TAG = "NetConnChangeHandler"; - private static Context context; - - public static void init(Context context) { - NetworkConnectionChangeHandler.context = context; - } - - public static void networkChangedDetected() { - if (NetworkUtils.isAutoDownloadAllowed()) { - Log.d(TAG, "auto-dl network available, starting auto-download"); - AutoDownloadManager.getInstance().autodownloadUndownloadedItems(context); - } else { // if new network is Wi-Fi, finish ongoing downloads, - // otherwise cancel all downloads - if (NetworkUtils.isNetworkRestricted()) { - Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads"); - DownloadServiceInterface.get().cancelAll(context); - } - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java deleted file mode 100644 index 6e5c3e84b..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.danoeh.antennapod.core.util.gui; - -import android.view.View; -import android.widget.ImageView; -import android.widget.ProgressBar; - -import de.danoeh.antennapod.core.R; - -/** - * Utility methods for the more_content_list_footer layout. - */ -public class MoreContentListFooterUtil { - - private final View root; - - private boolean loading; - - private Listener listener; - - public MoreContentListFooterUtil(View root) { - this.root = root; - root.setOnClickListener(v -> { - if (listener != null && !loading) { - listener.onClick(); - } - }); - } - - public void setLoadingState(boolean newState) { - final ImageView imageView = root.findViewById(R.id.imgExpand); - final ProgressBar progressBar = root.findViewById(R.id.progBar); - if (newState) { - imageView.setVisibility(View.GONE); - progressBar.setVisibility(View.VISIBLE); - } else { - imageView.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); - } - loading = newState; - } - - public void setClickListener(Listener l) { - listener = l; - } - - public interface Listener { - void onClick(); - } - - public View getRoot() { - return root; - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java deleted file mode 100644 index f763653a1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.danoeh.antennapod.core.util.gui; - -import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Build; - -public class PictureInPictureUtil { - private PictureInPictureUtil() { - } - - public static boolean supportsPictureInPicture(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - PackageManager packageManager = activity.getPackageManager(); - return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE); - } else { - return false; - } - } - - public static boolean isInPictureInPictureMode(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && supportsPictureInPicture(activity)) { - return activity.isInPictureInPictureMode(); - } else { - return false; - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/ShownotesCleaner.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/ShownotesCleaner.java deleted file mode 100644 index 7bf9257a1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/ShownotesCleaner.java +++ /dev/null @@ -1,208 +0,0 @@ -package de.danoeh.antennapod.core.util.gui; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import android.text.TextUtils; -import android.util.Log; -import android.util.TypedValue; - -import androidx.annotation.Nullable; -import org.apache.commons.io.IOUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.ui.common.Converter; - -/** - * Cleans up and prepares shownotes: - * - Guesses time stamps to make them clickable - * - Removes some formatting - */ -public class ShownotesCleaner { - private static final String TAG = "Timeline"; - - private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/(\\d+)"); - private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>"; - private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b"); - private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>"); - private static final String CSS_COLOR = "(?<=(\\s|;|^))color\\s*:([^;])*;"; - private static final String CSS_COMMENT = "/\\*.*?\\*/"; - - private final String rawShownotes; - private final String noShownotesLabel; - private final int playableDuration; - private final String webviewStyle; - - public ShownotesCleaner(Context context, @Nullable String rawShownotes, int playableDuration) { - this.rawShownotes = rawShownotes; - - noShownotesLabel = context.getString(R.string.no_shownotes_label); - this.playableDuration = playableDuration; - final String colorPrimary = colorToHtml(context, android.R.attr.textColorPrimary); - final String colorAccent = colorToHtml(context, R.attr.colorAccent); - final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, - context.getResources().getDisplayMetrics()); - String styleString = ""; - try { - InputStream templateStream = context.getAssets().open("shownotes-style.css"); - styleString = IOUtils.toString(templateStream, "UTF-8"); - } catch (IOException e) { - e.printStackTrace(); - } - webviewStyle = String.format(Locale.US, styleString, colorPrimary, colorAccent, - margin, margin, margin, margin); - } - - private String colorToHtml(Context context, int colorAttr) { - TypedArray res = context.getTheme().obtainStyledAttributes(new int[]{colorAttr}); - @ColorInt int col = res.getColor(0, 0); - final String color = "rgba(" + Color.red(col) + "," + Color.green(col) + "," - + Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")"; - res.recycle(); - return color; - } - - /** - * Applies an app-specific CSS stylesheet and adds timecode links (optional). - * <p/> - * This method does NOT change the original shownotes string of the shownotesProvider object and it should - * also not be changed by the caller. - * - * @return The processed HTML string. - */ - @NonNull - public String processShownotes() { - String shownotes = rawShownotes; - - if (TextUtils.isEmpty(shownotes)) { - Log.d(TAG, "shownotesProvider contained no shownotes. Returning 'no shownotes' message"); - shownotes = "<html><head></head><body><p id='apNoShownotes'>" + noShownotesLabel + "</p></body></html>"; - } - - // replace ASCII line breaks with HTML ones if shownotes don't contain HTML line breaks already - if (!LINE_BREAK_REGEX.matcher(shownotes).find() && !shownotes.contains("<p>")) { - shownotes = shownotes.replace("\n", "<br />"); - } - - Document document = Jsoup.parse(shownotes); - cleanCss(document); - document.head().appendElement("style").attr("type", "text/css").text(webviewStyle); - addTimecodes(document); - return document.toString(); - } - - /** - * Returns true if the given link is a timecode link. - */ - public static boolean isTimecodeLink(String link) { - return link != null && link.matches(TIMECODE_LINK_REGEX.pattern()); - } - - /** - * Returns the time in milliseconds that is attached to this link or -1 - * if the link is no valid timecode link. - */ - public static int getTimecodeLinkTime(String link) { - if (isTimecodeLink(link)) { - Matcher m = TIMECODE_LINK_REGEX.matcher(link); - - try { - if (m.find()) { - return Integer.parseInt(m.group(1)); - } - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - return -1; - } - - private void addTimecodes(Document document) { - Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX); - Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes"); - - if (elementsWithTimeCodes.size() == 0) { - // No elements with timecodes - return; - } - boolean useHourFormat = true; - - if (playableDuration != Integer.MAX_VALUE) { - - // We need to decide if we are going to treat short timecodes as HH:MM or MM:SS. To do - // so we will parse all the short timecodes and see if they fit in the duration. If one - // does not we will use MM:SS, otherwise all will be parsed as HH:MM. - for (Element element : elementsWithTimeCodes) { - Matcher matcherForElement = TIMECODE_REGEX.matcher(element.html()); - while (matcherForElement.find()) { - - // We only want short timecodes right now. - if (matcherForElement.group(1) == null) { - int time = Converter.durationStringShortToMs(matcherForElement.group(0), true); - - // If the parsed timecode is greater then the duration then we know we need to - // use the minute format so we are done. - if (time > playableDuration) { - useHourFormat = false; - break; - } - } - } - - if (!useHourFormat) { - break; - } - } - } - - for (Element element : elementsWithTimeCodes) { - - Matcher matcherForElement = TIMECODE_REGEX.matcher(element.html()); - StringBuffer buffer = new StringBuffer(); - - while (matcherForElement.find()) { - String group = matcherForElement.group(0); - - int time = matcherForElement.group(1) != null - ? Converter.durationStringLongToMs(group) - : Converter.durationStringShortToMs(group, useHourFormat); - - String replacementText = group; - if (time < playableDuration) { - replacementText = String.format(Locale.US, TIMECODE_LINK, time, group); - } - - matcherForElement.appendReplacement(buffer, replacementText); - } - - matcherForElement.appendTail(buffer); - element.html(buffer.toString()); - } - } - - private void cleanCss(Document document) { - for (Element element : document.getAllElements()) { - if (element.hasAttr("style")) { - element.attr("style", element.attr("style").replaceAll(CSS_COLOR, "")); - } else if (element.tagName().equals("style")) { - element.html(cleanStyleTag(element.html())); - } - } - } - - public static String cleanStyleTag(String oldCss) { - return oldCss.replaceAll(CSS_COMMENT, "").replaceAll(CSS_COLOR, ""); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java deleted file mode 100644 index 316608f2f..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java +++ /dev/null @@ -1,79 +0,0 @@ -package de.danoeh.antennapod.core.util.syndication; - -import android.net.Uri; -import androidx.collection.ArrayMap; -import android.text.TextUtils; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.File; -import java.io.IOException; -import java.util.Map; - -/** - * Finds RSS/Atom URLs in a HTML document using the auto-discovery techniques described here: - * <p/> - * http://www.rssboard.org/rss-autodiscovery - * <p/> - * http://blog.whatwg.org/feed-autodiscovery - */ -public class FeedDiscoverer { - - private static final String MIME_RSS = "application/rss+xml"; - private static final String MIME_ATOM = "application/atom+xml"; - - /** - * Discovers links to RSS and Atom feeds in the given File which must be a HTML document. - * - * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if - * a title cannot be found). - */ - public Map<String, String> findLinks(File in, String baseUrl) throws IOException { - return findLinks(Jsoup.parse(in), baseUrl); - } - - /** - * Discovers links to RSS and Atom feeds in the given File which must be a HTML document. - * - * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if - * a title cannot be found). - */ - public Map<String, String> findLinks(String in, String baseUrl) { - return findLinks(Jsoup.parse(in), baseUrl); - } - - private Map<String, String> findLinks(Document document, String baseUrl) { - Map<String, String> res = new ArrayMap<>(); - Elements links = document.head().getElementsByTag("link"); - for (Element link : links) { - String rel = link.attr("rel"); - String href = link.attr("href"); - if (!TextUtils.isEmpty(href) && - (rel.equals("alternate") || rel.equals("feed"))) { - String type = link.attr("type"); - if (type.equals(MIME_RSS) || type.equals(MIME_ATOM)) { - String title = link.attr("title"); - String processedUrl = processURL(baseUrl, href); - if (processedUrl != null) { - res.put(processedUrl, - (TextUtils.isEmpty(title)) ? href : title); - } - } - } - } - return res; - } - - private String processURL(String baseUrl, String strUrl) { - Uri uri = Uri.parse(strUrl); - if (uri.isRelative()) { - Uri res = Uri.parse(baseUrl).buildUpon().path(strUrl).build(); - return (res != null) ? res.toString() : null; - } else { - return strUrl; - } - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java deleted file mode 100644 index c5f0727c2..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java +++ /dev/null @@ -1,123 +0,0 @@ -package de.danoeh.antennapod.core.util.syndication; - -import android.text.TextUtils; - -import org.apache.commons.lang3.StringUtils; -import org.jsoup.Jsoup; -import org.jsoup.internal.StringUtil; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; -import org.jsoup.nodes.TextNode; -import org.jsoup.select.NodeTraversor; -import org.jsoup.select.NodeVisitor; - -import java.util.regex.Pattern; - -/** - * This class is based on <code>HtmlToPlainText</code> from jsoup's examples package. - * - * HTML to plain-text. This example program demonstrates the use of jsoup to convert HTML input to lightly-formatted - * plain-text. That is divergent from the general goal of jsoup's .text() methods, which is to get clean data from a - * scrape. - * <p> - * Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend. - * </p> - * <p> - * To invoke from the command line, assuming you've downloaded the jsoup jar to your current directory:</p> - * <p><code>java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]</code></p> - * where <i>url</i> is the URL to fetch, and <i>selector</i> is an optional CSS selector. - * - * @author Jonathan Hedley, jonathan@hedley.net - * @author AntennaPod open source community - */ -public class HtmlToPlainText { - - /** - * Use this method to strip off HTML encoding from given text. - * Replaces bullet points with *, ignores colors/bold/... - * - * @param str String with any encoding - * @return Human readable text with minimal HTML formatting - */ - public static String getPlainText(String str) { - if (!TextUtils.isEmpty(str) && isHtml(str)) { - HtmlToPlainText formatter = new HtmlToPlainText(); - Document feedDescription = Jsoup.parse(str); - str = StringUtils.trim(formatter.getPlainText(feedDescription)); - } else if (TextUtils.isEmpty(str)) { - str = ""; - } - - return str; - } - - /** - * Use this method to determine if a given text has any HTML tag - * - * @param str String to be tested for presence of HTML content - * @return <b>True</b> if text contains any HTML tags<br /><b>False</b> is no HTML tag is found - */ - private static boolean isHtml(String str) { - final String htmlTagPattern = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>"; - return Pattern.compile(htmlTagPattern).matcher(str).find(); - } - - /** - * Format an Element to plain-text - * @param element the root element to format - * @return formatted text - */ - public String getPlainText(Element element) { - FormattingVisitor formatter = new FormattingVisitor(); - // walk the DOM, and call .head() and .tail() for each node - NodeTraversor.traverse(formatter, element); - - return formatter.toString(); - } - - // the formatting rules, implemented in a breadth-first DOM traverse - private static class FormattingVisitor implements NodeVisitor { - - private final StringBuilder accum = new StringBuilder(); // holds the accumulated text - - // hit when the node is first seen - public void head(Node node, int depth) { - String name = node.nodeName(); - if (node instanceof TextNode) { - append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM. - } else if (name.equals("li")) { - append("\n * "); - } else if (name.equals("dt")) { - append(" "); - } else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5", "tr")) { - append("\n"); - } - } - - // hit when all of the node's children (if any) have been visited - public void tail(Node node, int depth) { - String name = node.nodeName(); - if (StringUtil.in(name, "br", "dd", "dt", "p", "h1", "h2", "h3", "h4", "h5")) { - append("\n"); - } else if (name.equals("a")) { - append(String.format(" <%s>", node.absUrl("href"))); - } - } - - // appends text to the string builder with a simple word wrap method - private void append(String text) { - if (text.equals(" ") && - (accum.length() == 0 || StringUtil.in(accum.substring(accum.length() - 1), " ", "\n"))) { - return; // don't accumulate long runs of empty spaces - } - - accum.append(text); - } - - @Override - public String toString() { - return accum.toString(); - } - } -} |