diff options
Diffstat (limited to 'core')
36 files changed, 562 insertions, 897 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java index ea8eb7871..062a6abac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java @@ -1,34 +1,22 @@ package de.danoeh.antennapod.core.feed; -public class SearchResult { - private final FeedComponent component; - /** Additional information (e.g. where it was found) */ - private String subtitle; - /** Higher value means more importance */ - private final int value; - - public SearchResult(FeedComponent component, int value, String subtitle) { - super(); - this.component = component; - this.value = value; - this.subtitle = subtitle; - } +import de.danoeh.antennapod.core.storage.SearchLocation; - public FeedComponent getComponent() { - return component; - } - - public String getSubtitle() { - return subtitle; - } +public class SearchResult { + private final FeedComponent component; + private SearchLocation location; - public void setSubtitle(String subtitle) { - this.subtitle = subtitle; - } + public SearchResult(FeedComponent component, SearchLocation location) { + super(); + this.component = component; + this.location = location; + } - public int getValue() { - return value; - } - + public FeedComponent getComponent() { + return component; + } + public SearchLocation getLocation() { + return location; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java index f02334af5..8a13944e1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java @@ -11,6 +11,7 @@ import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; +import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -49,7 +50,6 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> { if (internalClient == null) { OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder(); builder.interceptors().add(new NetworkAllowanceInterceptor()); - builder.interceptors().add(new BasicAuthenticationInterceptor()); internalClient = builder.build(); } } @@ -123,48 +123,5 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> { .build(); } } - - } - - private static class BasicAuthenticationInterceptor implements Interceptor { - - @Override - public Response intercept(Chain chain) throws IOException { - Request request = chain.request(); - String url = request.url().toString(); - String authentication = DBReader.getImageAuthentication(url); - - if(TextUtils.isEmpty(authentication)) { - Log.d(TAG, "no credentials for '" + url + "'"); - return chain.proceed(request); - } - - // add authentication - String[] auth = authentication.split(":"); - if (auth.length != 2) { - Log.d(TAG, "Invalid credentials for '" + url + "'"); - return chain.proceed(request); - } - - String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1"); - Request newRequest = request - .newBuilder() - .addHeader("Authorization", credentials) - .build(); - Log.d(TAG, "Basic authentication with ISO-8859-1 encoding"); - Response response = chain.proceed(newRequest); - if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { - credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "UTF-8"); - newRequest = request - .newBuilder() - .addHeader("Authorization", credentials) - .build(); - Log.d(TAG, "Basic authentication with UTF-8 encoding"); - return chain.proceed(newRequest); - } else { - return response; - } - } } - }
\ No newline at end of file diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/BasicAuthorizationInterceptor.java b/core/src/main/java/de/danoeh/antennapod/core/service/BasicAuthorizationInterceptor.java new file mode 100644 index 000000000..394eb3943 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/BasicAuthorizationInterceptor.java @@ -0,0 +1,67 @@ +package de.danoeh.antennapod.core.service; + +import android.text.TextUtils; +import android.util.Log; +import androidx.annotation.NonNull; +import de.danoeh.antennapod.core.service.download.DownloadRequest; +import de.danoeh.antennapod.core.service.download.HttpDownloader; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.URIUtil; +import java.io.IOException; +import java.net.HttpURLConnection; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class BasicAuthorizationInterceptor implements Interceptor { + private static final String TAG = "BasicAuthInterceptor"; + + @Override + @NonNull + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + + Response response = chain.proceed(request); + + if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) { + return response; + } + + String userInfo; + if (request.tag() instanceof DownloadRequest) { + DownloadRequest downloadRequest = (DownloadRequest) request.tag(); + userInfo = URIUtil.getURIFromRequestUrl(downloadRequest.getSource()).getUserInfo(); + if (TextUtils.isEmpty(userInfo)) { + userInfo = downloadRequest.getUsername() + ":" + downloadRequest.getPassword(); + } + } else { + userInfo = DBReader.getImageAuthentication(request.url().toString()); + } + + if (TextUtils.isEmpty(userInfo)) { + Log.d(TAG, "no credentials for '" + request.url() + "'"); + return response; + } + + String[] parts = userInfo.split(":"); + if (parts.length != 2) { + Log.d(TAG, "Invalid credentials for '" + request.url() + "'"); + return response; + } + + Request.Builder newRequest = request.newBuilder(); + Log.d(TAG, "Authorization failed, re-trying with ISO-8859-1 encoded credentials"); + String credentials = HttpDownloader.encodeCredentials(parts[0], parts[1], "ISO-8859-1"); + newRequest.header("Authorization", credentials); + response = chain.proceed(newRequest.build()); + + if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) { + return response; + } + + Log.d(TAG, "Authorization failed, re-trying with UTF-8 encoded credentials"); + credentials = HttpDownloader.encodeCredentials(parts[0], parts[1], "UTF-8"); + newRequest.header("Authorization", credentials); + return chain.proceed(newRequest.build()); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java index b254cfc59..e4d9ff361 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java @@ -38,6 +38,7 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.gui.NotificationUtils; /** @@ -185,15 +186,14 @@ public class GpodnetSyncService extends SafeJobIntentService { // local changes are always superior to remote changes! // add subscription if (1) not already subscribed and (2) not just unsubscribed for (String downloadUrl : changes.getAdded()) { - if (!localSubscriptions.contains(downloadUrl) && - !localRemoved.contains(downloadUrl)) { + if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !localRemoved.contains(downloadUrl)) { Feed feed = new Feed(downloadUrl, null); DownloadRequester.getInstance().downloadFeed(this, feed); } } // remove subscription if not just subscribed (again) for (String downloadUrl : changes.getRemoved()) { - if(!localAdded.contains(downloadUrl)) { + if (!localAdded.contains(downloadUrl)) { DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java index 2d9de1894..e6f2176f7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java @@ -159,14 +159,10 @@ public class PlayerWidgetJobService extends SafeJobIntentService { if (status == PlayerStatus.PLAYING) { views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp); - if (Build.VERSION.SDK_INT >= 15) { - views.setContentDescription(R.id.butPlay, getString(R.string.pause_label)); - } + views.setContentDescription(R.id.butPlay, getString(R.string.pause_label)); } else { views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp); - if (Build.VERSION.SDK_INT >= 15) { - views.setContentDescription(R.id.butPlay, getString(R.string.play_label)); - } + views.setContentDescription(R.id.butPlay, getString(R.string.play_label)); } views.setOnClickPendingIntent(R.id.butPlay, createMediaButtonIntent()); } else { diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index 672cbe9e1..8f368c76e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; +import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; import java.io.IOException; import java.net.CookieManager; import java.net.CookiePolicy; @@ -112,6 +113,7 @@ public class AntennapodHttpClient { } return response; }); + builder.interceptors().add(new BasicAuthorizationInterceptor()); // set cookie handler CookieManager cm = new CookieManager(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java index 7c05293c6..f7c338729 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java @@ -167,7 +167,7 @@ public class DownloadService extends Service { Notification notification = notificationManager.updateNotifications( requester.getNumberOfDownloads(), downloads); startForeground(NOTIFICATION_ID, notification); - onDownloadQueued(intent); + syncExecutor.execute(() -> onDownloadQueued(intent)); } else if (numberOfDownloads.get() == 0) { stopSelf(); } else { @@ -477,13 +477,13 @@ public class DownloadService extends Service { && isEnqueued(request, itemsEnqueued)) { request.setMediaEnqueued(true); } - downloads.add(downloader); - downloadExecutor.submit(downloader); - - postDownloaders(); + handler.post(() -> { + downloads.add(downloader); + downloadExecutor.submit(downloader); + postDownloaders(); + }); } - - queryDownloads(); + handler.post(this::queryDownloads); } private static boolean isEnqueued(@NonNull DownloadRequest request, diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java index 8abfa3da3..551a20bf2 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; +import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; import org.apache.commons.io.IOUtils; import java.io.BufferedInputStream; @@ -54,9 +55,7 @@ public class HttpDownloader extends Downloader { return; } - OkHttpClient.Builder httpClientBuilder = AntennapodHttpClient.newBuilder(); - httpClientBuilder.interceptors().add(new BasicAuthorizationInterceptor(request)); - OkHttpClient httpClient = httpClientBuilder.build(); + OkHttpClient httpClient = AntennapodHttpClient.getHttpClient(); RandomAccessFile out = null; InputStream connection; ResponseBody responseBody = null; @@ -65,6 +64,7 @@ public class HttpDownloader extends Downloader { final URI uri = URIUtil.getURIFromRequestUrl(request.getSource()); Request.Builder httpReq = new Request.Builder().url(uri.toURL()) .header("User-Agent", ClientConfig.USER_AGENT); + httpReq.tag(request); if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { // set header explicitly so that okhttp doesn't do transparent gzip Log.d(TAG, "addHeader(\"Accept-Encoding\", \"identity\")"); @@ -308,63 +308,4 @@ public class HttpDownloader extends Downloader { throw new AssertionError(e); } } - - private static class BasicAuthorizationInterceptor implements Interceptor { - - private final DownloadRequest downloadRequest; - - public BasicAuthorizationInterceptor(DownloadRequest downloadRequest) { - this.downloadRequest = downloadRequest; - } - - @Override - public Response intercept(Chain chain) throws IOException { - Request request = chain.request(); - String userInfo = URIUtil.getURIFromRequestUrl(downloadRequest.getSource()).getUserInfo(); - - Response response = chain.proceed(request); - - if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) { - return response; - } - - Request.Builder newRequest = request.newBuilder(); - - Log.d(TAG, "Authorization failed, re-trying with ISO-8859-1 encoded credentials"); - if (userInfo != null) { - String[] parts = userInfo.split(":"); - if (parts.length == 2) { - String credentials = encodeCredentials(parts[0], parts[1], "ISO-8859-1"); - newRequest.header("Authorization", credentials); - } - } else if (!TextUtils.isEmpty(downloadRequest.getUsername()) && downloadRequest.getPassword() != null) { - String credentials = encodeCredentials(downloadRequest.getUsername(), downloadRequest.getPassword(), - "ISO-8859-1"); - newRequest.header("Authorization", credentials); - } - - response = chain.proceed(newRequest.build()); - - if (response.code() != HttpURLConnection.HTTP_UNAUTHORIZED) { - return response; - } - - Log.d(TAG, "Authorization failed, re-trying with UTF-8 encoded credentials"); - if (userInfo != null) { - String[] parts = userInfo.split(":"); - if (parts.length == 2) { - String credentials = encodeCredentials(parts[0], parts[1], "UTF-8"); - newRequest.header("Authorization", credentials); - } - } else if (!TextUtils.isEmpty(downloadRequest.getUsername()) && downloadRequest.getPassword() != null) { - String credentials = encodeCredentials(downloadRequest.getUsername(), downloadRequest.getPassword(), - "UTF-8"); - newRequest.header("Authorization", credentials); - } - - return chain.proceed(newRequest.build()); - } - - } - } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java index c250cd17f..8f7e84561 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java @@ -16,8 +16,9 @@ import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioAttributes; -import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; @@ -71,7 +72,7 @@ public class ExoPlayerWrapper implements IPlayer { loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true); SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext), new DefaultTrackSelector(), loadControl.createDefaultLoadControl()); - p.setSeekParameters(SeekParameters.PREVIOUS_SYNC); + p.setSeekParameters(SeekParameters.EXACT); p.addListener(new Player.EventListener() { @Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { @@ -170,7 +171,7 @@ public class ExoPlayerWrapper implements IPlayer { @Override public void prepare() throws IllegalStateException { - mExoPlayer.prepare(mediaSource); + mExoPlayer.prepare(mediaSource, false, true); } @Override @@ -216,7 +217,9 @@ public class ExoPlayerWrapper implements IPlayer { DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, true); DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory); - ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory); + DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); + extractorsFactory.setConstantBitrateSeekingEnabled(true); + ProgressiveMediaSource.Factory f = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory); mediaSource = f.createMediaSource(Uri.parse(s)); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java index 6219fe78c..174b43846 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java @@ -25,10 +25,11 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; -import de.danoeh.antennapod.core.util.IntList; import de.danoeh.antennapod.core.util.TimeSpeedConverter; import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.playback.Playable; +import java.util.ArrayList; +import org.apache.commons.lang3.ArrayUtils; public class PlaybackServiceNotificationBuilder { private static final String TAG = "PlaybackSrvNotification"; @@ -151,7 +152,7 @@ public class PlaybackServiceNotificationBuilder { private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { - IntList compactActionList = new IntList(); + ArrayList<Integer> compactActionList = new ArrayList<>(); int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction @@ -218,7 +219,7 @@ public class PlaybackServiceNotificationBuilder { KeyEvent.KEYCODE_MEDIA_STOP, numActions); notification.setStyle(new androidx.media.app.NotificationCompat.MediaStyle() .setMediaSession(mediaSessionToken) - .setShowActionsInCompactView(compactActionList.toArray()) + .setShowActionsInCompactView(ArrayUtils.toPrimitive(compactActionList.toArray(new Integer[0]))) .setShowCancelButton(true) .setCancelButtonIntent(stopButtonPendingIntent)); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java index d029e7bfb..6e4054009 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java @@ -50,7 +50,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { List<FeedItem> candidates; final List<FeedItem> queue = DBReader.getQueue(); - final List<FeedItem> newItems = DBReader.getNewItemsList(); + final List<FeedItem> newItems = DBReader.getNewItemsList(0, Integer.MAX_VALUE); candidates = new ArrayList<>(queue.size() + newItems.size()); candidates.addAll(queue); for (FeedItem newItem : newItems) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index 8b87d7c54..e6d21794c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -353,16 +353,18 @@ public final class DBReader { * Loads a list of FeedItems that are considered new. * Excludes items from feeds that do not have keep updated enabled. * + * @param offset The first episode that should be loaded. + * @param limit The maximum number of episodes that should be loaded. * @return A list of FeedItems that are considered new. */ - public static List<FeedItem> getNewItemsList() { + public static List<FeedItem> getNewItemsList(int offset, int limit) { Log.d(TAG, "getNewItemsList() called"); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); Cursor cursor = null; try { - cursor = adapter.getNewItemsCursor(); + cursor = adapter.getNewItemsCursor(offset, limit); List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); loadAdditionalFeedItemListData(items); return items; @@ -374,14 +376,21 @@ public final class DBReader { } } - public static List<FeedItem> getFavoriteItemsList() { + /** + * Loads a list of favorite items. + * + * @param offset The first episode that should be loaded. + * @param limit The maximum number of episodes that should be loaded. + * @return A list of FeedItems that are marked as favorite. + */ + public static List<FeedItem> getFavoriteItemsList(int offset, int limit) { Log.d(TAG, "getFavoriteItemsList() called"); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); Cursor cursor = null; try { - cursor = adapter.getFavoritesCursor(); + cursor = adapter.getFavoritesCursor(offset, limit); List<FeedItem> items = extractItemlistFromCursor(adapter, cursor); loadAdditionalFeedItemListData(items); return items; @@ -400,7 +409,7 @@ public final class DBReader { adapter.open(); Cursor cursor = null; try { - cursor = adapter.getFavoritesCursor(); + cursor = adapter.getFavoritesCursor(0, Integer.MAX_VALUE); LongList favoriteIDs = new LongList(cursor.getCount()); while (cursor.moveToNext()) { favoriteIDs.add(cursor.getLong(0)); @@ -510,17 +519,6 @@ public final class DBReader { /** * Loads the download log for a particular feed from the database. * - * @param feed Feed for which the download log is loaded - * @return A list with DownloadStatus objects that represent the feed's download log, - * newest events first. - */ - public static List<DownloadStatus> getFeedDownloadLog(Feed feed) { - return getFeedDownloadLog(feed.getId()); - } - - /** - * Loads the download log for a particular feed from the database. - * * @param feedId Feed id for which the download log is loaded * @return A list with DownloadStatus objects that represent the feed's download log, * newest events first. @@ -870,12 +868,10 @@ public final class DBReader { /** * Searches the DB for statistics * - * @param sortByCountAll If true, the statistic items will be sorted according to the - * countAll calculation time * @return The StatisticsInfo object */ @NonNull - public static StatisticsData getStatistics(boolean sortByCountAll) { + public static StatisticsData getStatistics() { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); @@ -891,6 +887,7 @@ public final class DBReader { long episodes = 0; long episodesStarted = 0; long episodesStartedIncludingMarked = 0; + long totalDownloadSize = 0; List<FeedItem> items = getFeed(feed.getId()).getItems(); for (FeedItem item : items) { FeedMedia media = item.getMedia(); @@ -915,43 +912,24 @@ public final class DBReader { } feedTotalTime += media.getDuration() / 1000; + + if (media.isDownloaded()) { + totalDownloadSize = totalDownloadSize + media.getSize(); + } + episodes++; } feedTime.add(new StatisticsItem( feed, feedTotalTime, feedPlayedTime, feedPlayedTimeCountAll, episodes, - episodesStarted, episodesStartedIncludingMarked)); + episodesStarted, episodesStartedIncludingMarked, totalDownloadSize)); totalTime += feedPlayedTime; totalTimeCountAll += feedPlayedTimeCountAll; } - if (sortByCountAll) { - Collections.sort(feedTime, (item1, item2) -> - compareLong(item1.timePlayedCountAll, item2.timePlayedCountAll)); - } else { - Collections.sort(feedTime, (item1, item2) -> - compareLong(item1.timePlayed, item2.timePlayed)); - } - adapter.close(); return new StatisticsData(totalTime, totalTimeCountAll, feedTime); } - /** - * Compares two {@code long} values. Long.compare() is not available before API 19 - * - * @return 0 if long1 = long2, less than 0 if long1 < long2, - * and greater than 0 if long1 > long2. - */ - private static int compareLong(long long1, long long2) { - if (long1 > long2) { - return -1; - } else if (long1 < long2) { - return 1; - } else { - return 0; - } - } - public static class StatisticsData { /** * Simply sums up time of podcasts that are marked as played @@ -963,12 +941,12 @@ public final class DBReader { */ public final long totalTime; - public final List<StatisticsItem> feedTime; + public final List<StatisticsItem> feeds; - public StatisticsData(long totalTime, long totalTimeCountAll, List<StatisticsItem> feedTime) { + public StatisticsData(long totalTime, long totalTimeCountAll, List<StatisticsItem> feeds) { this.totalTime = totalTime; this.totalTimeCountAll = totalTimeCountAll; - this.feedTime = feedTime; + this.feeds = feeds; } } @@ -993,9 +971,14 @@ public final class DBReader { * All episodes that are marked as played (or have position != 0) */ public final long episodesStartedIncludingMarked; + /** + * Simply sums up the size of download podcasts + */ + public final long totalDownloadSize; public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll, - long episodes, long episodesStarted, long episodesStartedIncludingMarked) { + long episodes, long episodesStarted, long episodesStartedIncludingMarked, + long totalDownloadSize) { this.feed = feed; this.time = time; this.timePlayed = timePlayed; @@ -1003,6 +986,7 @@ public final class DBReader { this.episodes = episodes; this.episodesStarted = episodesStarted; this.episodesStartedIncludingMarked = episodesStartedIncludingMarked; + this.totalDownloadSize = totalDownloadSize; } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index 6e72a762a..91f656bf1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -379,18 +379,6 @@ public final class DBTasks { return result; } - /** - * Loads the queue from the database and checks if the specified FeedItem is in the queue. - * This method should NOT be executed in the GUI thread. - * - * @param context Used for accessing the DB. - * @param feedItemId ID of the FeedItem - */ - public static boolean isInQueue(Context context, final long feedItemId) { - LongList queue = DBReader.getQueueIDList(); - return queue.contains(feedItemId); - } - private static Feed searchFeedByIdentifyingValueOrID(PodDBAdapter adapter, Feed feed) { if (feed.getId() != 0) { @@ -496,17 +484,22 @@ public final class DBTasks { // item is new item.setFeed(savedFeed); item.setAutoDownload(savedFeed.getPreferences().getAutoDownload()); - savedFeed.getItems().add(idx, item); + + if (idx >= savedFeed.getItems().size()) { + savedFeed.getItems().add(item); + } else { + savedFeed.getItems().add(idx, item); + } // only mark the item new if it was published after or at the same time // as the most recent item // (if the most recent date is null then we can assume there are no items // and this is the first, hence 'new') - if (priorMostRecentDate == null || - priorMostRecentDate.before(item.getPubDate()) || - priorMostRecentDate.equals(item.getPubDate())) { - Log.d(TAG, "Marking item published on " + item.getPubDate() + - " new, prior most recent date = " + priorMostRecentDate); + if (priorMostRecentDate == null + || priorMostRecentDate.before(item.getPubDate()) + || priorMostRecentDate.equals(item.getPubDate())) { + Log.d(TAG, "Marking item published on " + item.getPubDate() + + " new, prior most recent date = " + priorMostRecentDate); item.setNew(); } } else { @@ -538,140 +531,20 @@ public final class DBTasks { } /** - * Searches the titles of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask<List<FeedItem>> searchFeedItemTitle(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemTitles(feedID, - query); - List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the authors of FeedItems of a specific Feed for a given - * string. + * Searches the FeedItems of a specific Feed for a given string. * * @param context Used for accessing the DB. * @param feedID The id of the feed whose items should be searched. * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. + * @return A FutureTask object that executes the search request + * and returns the search result as a List of FeedItems. */ - public static FutureTask<List<FeedItem>> searchFeedItemAuthor(final Context context, + public static FutureTask<List<FeedItem>> searchFeedItems(final Context context, final long feedID, final String query) { return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { @Override public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemAuthors(feedID, - query); - List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the feed identifiers of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string. - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask<List<FeedItem>> searchFeedItemFeedIdentifier(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemFeedIdentifiers(feedID, - query); - List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the descriptions of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask<List<FeedItem>> searchFeedItemDescription(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemDescriptions(feedID, - query); - List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches the contentEncoded-value of FeedItems of a specific Feed for a given - * string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask<List<FeedItem>> searchFeedItemContentEncoded(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemContentEncoded(feedID, - query); - List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); - DBReader.loadAdditionalFeedItemListData(items); - setResult(items); - searchResult.close(); - } - }); - } - - /** - * Searches chapters of the FeedItems of a specific Feed for a given string. - * - * @param context Used for accessing the DB. - * @param feedID The id of the feed whose items should be searched. - * @param query The search string - * @return A FutureTask object that executes the search request and returns the search result as a List of FeedItems. - */ - public static FutureTask<List<FeedItem>> searchFeedItemChapters(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { - @Override - public void execute(PodDBAdapter adapter) { - Cursor searchResult = adapter.searchItemChapters(feedID, - query); + Cursor searchResult = adapter.searchItems(feedID, query); List<FeedItem> items = DBReader.extractItemlistFromCursor(searchResult); DBReader.loadAdditionalFeedItemListData(items); setResult(items); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index 0c8d20007..1d9e33d0e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.storage; import android.content.Context; import androidx.annotation.NonNull; +import de.danoeh.antennapod.core.feed.Chapter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -17,13 +18,12 @@ import de.danoeh.antennapod.core.feed.SearchResult; import de.danoeh.antennapod.core.util.comparator.InReverseChronologicalOrder; /** - * Performs search on Feeds and FeedItems + * Performs search on Feeds and FeedItems. */ public class FeedSearcher { - private FeedSearcher(){} - - private static final String TAG = "FeedSearcher"; + private FeedSearcher() { + } /** * Search through a feed, or all feeds, for episodes that match the query in either the title, @@ -31,52 +31,54 @@ public class FeedSearcher { * show notes. The list of resulting episodes also describes where the first match occurred * (title, chapters, or show notes). * - * @param context + * @param context Used for database access * @param query search query * @param selectedFeed feed to search, 0 to search through all feeds * @return list of episodes containing the query */ @NonNull - public static List<SearchResult> performSearch(final Context context, - final String query, final long selectedFeed) { - final int values[] = {2, 1, 0, 0, 0, 0}; - final String[] subtitles = {context.getString(R.string.found_in_title_label), - context.getString(R.string.found_in_chapters_label), - context.getString(R.string.found_in_shownotes_label), - context.getString(R.string.found_in_shownotes_label), - context.getString(R.string.found_in_authors_label), - context.getString(R.string.found_in_feeds_label)}; - + public static List<SearchResult> performSearch(final Context context, final String query, final long selectedFeed) { final List<SearchResult> result = new ArrayList<>(); - - List<FutureTask<List<FeedItem>>> tasks = new ArrayList<>(); - tasks.add(DBTasks.searchFeedItemTitle(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemChapters(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemDescription(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemContentEncoded(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemAuthor(context, selectedFeed, query)); - tasks.add(DBTasks.searchFeedItemFeedIdentifier(context, selectedFeed, query)); - - for (FutureTask<List<FeedItem>> task : tasks) { - task.run(); - } try { - Set<Long> set = new HashSet<>(); - - for (int i = 0; i < tasks.size(); i++) { - FutureTask<List<FeedItem>> task = tasks.get(i); - List<FeedItem> items = task.get(); - for (FeedItem item : items) { - if (!set.contains(item.getId())) { // to prevent duplicate results - result.add(new SearchResult(item, values[i], subtitles[i])); - set.add(item.getId()); - } + FutureTask<List<FeedItem>> searchTask = DBTasks.searchFeedItems(context, selectedFeed, query); + searchTask.run(); + final List<FeedItem> items = searchTask.get(); + for (FeedItem item : items) { + SearchLocation location; + if (safeContains(item.getTitle(), query)) { + location = SearchLocation.TITLE; + } else if (safeContains(item.getContentEncoded(), query)) { + location = SearchLocation.SHOWNOTES; + } else if (safeContains(item.getDescription(), query)) { + location = SearchLocation.SHOWNOTES; + } else if (safeContains(item.getChapters(), query)) { + location = SearchLocation.CHAPTERS; + } else if (safeContains(item.getFeed().getAuthor(), query)) { + location = SearchLocation.AUTHORS; + } else { + location = SearchLocation.FEED; } + result.add(new SearchResult(item, location)); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } - Collections.sort(result, new InReverseChronologicalOrder()); return result; } + + private static boolean safeContains(String haystack, String needle) { + return haystack != null && haystack.contains(needle); + } + + private static boolean safeContains(List<Chapter> haystack, String needle) { + if (haystack == null) { + return false; + } + for (Chapter chapter : haystack) { + if (safeContains(chapter.getTitle(), needle)) { + return true; + } + } + return false; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 6f1f3e65d..17b79a3da 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -907,49 +907,6 @@ public class PodDBAdapter { null, null); } - /** - * Returns a cursor for a DB query in the FeedImages table for given IDs. - * - * @param imageIds IDs of the images - * @return The cursor of the query - */ - public final Cursor getImageCursor(String... imageIds) { - int length = imageIds.length; - if (length > IN_OPERATOR_MAXIMUM) { - Log.w(TAG, "Length of id array is larger than " - + IN_OPERATOR_MAXIMUM + ". Creating multiple cursors"); - int numCursors = (int) (((double) length) / (IN_OPERATOR_MAXIMUM)) + 1; - Cursor[] cursors = new Cursor[numCursors]; - for (int i = 0; i < numCursors; i++) { - int neededLength; - String[] parts; - final int elementsLeft = length - i * IN_OPERATOR_MAXIMUM; - - if (elementsLeft >= IN_OPERATOR_MAXIMUM) { - neededLength = IN_OPERATOR_MAXIMUM; - parts = Arrays.copyOfRange(imageIds, i - * IN_OPERATOR_MAXIMUM, (i + 1) - * IN_OPERATOR_MAXIMUM); - } else { - neededLength = elementsLeft; - parts = Arrays.copyOfRange(imageIds, i - * IN_OPERATOR_MAXIMUM, (i * IN_OPERATOR_MAXIMUM) - + neededLength); - } - - cursors[i] = db.rawQuery("SELECT * FROM " - + TABLE_NAME_FEED_IMAGES + " WHERE " + KEY_ID + " IN " - + buildInOperator(neededLength), parts); - } - Cursor result = new MergeCursor(cursors); - result.moveToFirst(); - return result; - } else { - return db.query(TABLE_NAME_FEED_IMAGES, null, KEY_ID + " IN " - + buildInOperator(length), imageIds, null, null, null); - } - } - public final Cursor getSimpleChaptersOfFeedItemCursor(final FeedItem item) { return db.query(TABLE_NAME_SIMPLECHAPTERS, null, KEY_FEEDITEM + "=?", new String[]{String.valueOf(item.getId())}, null, @@ -990,14 +947,17 @@ public class PodDBAdapter { } - public final Cursor getFavoritesCursor() { + public final Cursor getFavoritesCursor(int offset, int limit) { Object[] args = new String[]{ SEL_FI_SMALL_STR, TABLE_NAME_FEED_ITEMS, TABLE_NAME_FAVORITES, TABLE_NAME_FEED_ITEMS + "." + KEY_ID, TABLE_NAME_FAVORITES + "." + KEY_FEEDITEM, - TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE}; - String query = String.format("SELECT %s FROM %s INNER JOIN %s ON %s=%s ORDER BY %s DESC", args); + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE, + String.valueOf(offset), + String.valueOf(limit) + }; + String query = String.format("SELECT %s FROM %s INNER JOIN %s ON %s=%s ORDER BY %s DESC LIMIT %s, %s", args); return db.rawQuery(query, null); } @@ -1030,16 +990,19 @@ public class PodDBAdapter { * Excludes those feeds that do not have 'Keep Updated' enabled. * The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection. */ - public final Cursor getNewItemsCursor() { + public final Cursor getNewItemsCursor(int offset, int limit) { Object[] args = new String[]{ SEL_FI_SMALL_STR, TABLE_NAME_FEED_ITEMS, TABLE_NAME_FEEDS, TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID, TABLE_NAME_FEED_ITEMS + "." + KEY_READ + "=" + FeedItem.NEW + " AND " + TABLE_NAME_FEEDS + "." + KEY_KEEP_UPDATED + " > 0", - KEY_PUBDATE + " DESC" + KEY_PUBDATE + " DESC", + String.valueOf(offset), + String.valueOf(limit) }; - final String query = String.format("SELECT %s FROM %s INNER JOIN %s ON %s WHERE %s ORDER BY %s", args); + final String query = String.format("SELECT %s FROM %s INNER JOIN %s ON %s WHERE %s " + + "ORDER BY %s LIMIT %s, %s", args); return db.rawQuery(query, null); } @@ -1293,133 +1256,39 @@ public class PodDBAdapter { } /** - * Searches for the given query in the description of all items or the items + * Searches for the given query in various values of all items or the items * of a specified feed. * * @return A cursor with all search results in SEL_FI_EXTRA selection. */ - public Cursor searchItemDescriptions(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_DESCRIPTION + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); - } else { - // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_DESCRIPTION + " LIKE '%" + prepareSearchQuery(query) - + "%'", null, null, null, null - ); - } - } - - /** - * Searches for the given query in the content-encoded field of all items or - * the items of a specified feed. - * - * @return A cursor with all search results in SEL_FI_EXTRA selection. - */ - public Cursor searchItemContentEncoded(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_CONTENT_ENCODED + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); - } else { - // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_CONTENT_ENCODED + " LIKE '%" - + prepareSearchQuery(query) + "%'", null, null, - null, null - ); - } - } - - public Cursor searchItemTitles(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED - + "=? AND " + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(query) + "%'", - new String[]{String.valueOf(feedID)}, null, null, - null - ); - } else { - // search through all items - return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, - KEY_TITLE + " LIKE '%" - + prepareSearchQuery(query) + "%'", null, null, - null, null - ); - } - } - - public Cursor searchItemAuthors(long feedID, String query) { - if (feedID != 0) { - // search items in specific feed - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED - + "=? AND " + KEY_AUTHOR + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - new String[]{String.valueOf(feedID)} - ); - } else { - // search through all items - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_AUTHOR + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - null - ); - } - } + public Cursor searchItems(long feedID, String searchQuery) { + String preparedQuery = prepareSearchQuery(searchQuery); - public Cursor searchItemFeedIdentifiers(long feedID, String query) { + String queryFeedId = ""; if (feedID != 0) { // search items in specific feed - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED - + "=? AND " + KEY_FEED_IDENTIFIER + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - new String[]{String.valueOf(feedID)} - ); + queryFeedId = KEY_FEED + " = " + feedID; } else { // search through all items - return db.rawQuery("SELECT " + TextUtils.join(", ", FEEDITEM_SEL_FI_SMALL) + " FROM " + TABLE_NAME_FEED_ITEMS - + " JOIN " + TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID - + " WHERE " + KEY_FEED_IDENTIFIER + " LIKE '%" - + prepareSearchQuery(query) + "%' ORDER BY " - + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + " DESC", - null - ); + queryFeedId = "1 = 1"; } - } - public Cursor searchItemChapters(long feedID, String searchQuery) { - final String query; - if (feedID != 0) { - query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + " INNER JOIN " + - TABLE_NAME_SIMPLECHAPTERS + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + "=" + - TABLE_NAME_FEED_ITEMS + "." + KEY_ID + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + - feedID + " AND " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(searchQuery) + "%'"; - } else { - query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + " INNER JOIN " + - TABLE_NAME_SIMPLECHAPTERS + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + "=" + - TABLE_NAME_FEED_ITEMS + "." + KEY_ID + " WHERE " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" - + prepareSearchQuery(searchQuery) + "%'"; - } + String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + + " LEFT JOIN " + TABLE_NAME_SIMPLECHAPTERS + + " ON " + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_FEEDITEM + + "=" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + + " LEFT JOIN " + TABLE_NAME_FEEDS + + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + + "=" + TABLE_NAME_FEEDS + "." + KEY_ID + + " WHERE " + queryFeedId + " AND (" + + TABLE_NAME_FEED_ITEMS + "." + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEED_ITEMS + "." + KEY_CONTENT_ENCODED + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEED_ITEMS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_SIMPLECHAPTERS + "." + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEEDS + "." + KEY_AUTHOR + " LIKE '%" + preparedQuery + "%' OR " + + TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER + " LIKE '%" + preparedQuery + "%'" + + ") ORDER BY " + KEY_PUBDATE + " DESC " + + "LIMIT 500"; return db.rawQuery(query, null); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java b/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java new file mode 100644 index 000000000..fabe85b2c --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/SearchLocation.java @@ -0,0 +1,21 @@ +package de.danoeh.antennapod.core.storage; + +import androidx.annotation.StringRes; +import de.danoeh.antennapod.core.R; + +public enum SearchLocation { + TITLE(R.string.found_in_title_label), + CHAPTERS(R.string.found_in_chapters_label), + SHOWNOTES(R.string.found_in_shownotes_label), + AUTHORS(R.string.found_in_authors_label), + FEED(R.string.found_in_feeds_label); + + private int description; + SearchLocation(@StringRes int description) { + this.description = description; + } + + public @StringRes int getDescription() { + return description; + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java deleted file mode 100644 index 1da5417c1..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java +++ /dev/null @@ -1,240 +0,0 @@ -package de.danoeh.antennapod.core.util; - -import java.util.Arrays; - -/** - * Fast and memory efficient int list - */ -public final class IntList { - - private int[] values; - private int size; - - /** - * Constructs an empty instance with a default initial capacity. - */ - public IntList() { - this(4); - } - - /** - * Constructs an empty instance. - * - * @param initialCapacity {@code >= 0;} initial capacity of the list - */ - private IntList(int initialCapacity) { - if(initialCapacity < 0) { - throw new IllegalArgumentException("initial capacity must be 0 or higher"); - } - values = new int[initialCapacity]; - size = 0; - } - - @Override - public int hashCode() { - int hashCode = 1; - for (int i = 0; i < size; i++) { - int value = values[i]; - hashCode = 31 * hashCode + value; - } - return hashCode; - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (! (other instanceof IntList)) { - return false; - } - IntList otherList = (IntList) other; - if (size != otherList.size) { - return false; - } - for (int i = 0; i < size; i++) { - if (values[i] != otherList.values[i]) { - return false; - } - } - return true; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(size * 5 + 10); - sb.append("IntList{"); - for (int i = 0; i < size; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(values[i]); - } - sb.append("}"); - return sb.toString(); - } - - /** - * Gets the number of elements in this list. - */ - public int size() { - return size; - } - - /** - * Gets the indicated value. - * - * @param n {@code >= 0, < size();} which element - * @return the indicated element's value - */ - public int get(int n) { - if (n >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } else if(n < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - return values[n]; - } - - /** - * Sets the value at the given index. - * - * @param index the index at which to put the specified object. - * @param value the object to add. - * @return the previous element at the index. - */ - public int set(int index, int value) { - if (index >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } else if(index < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - int result = values[index]; - values[index] = value; - return result; - } - - /** - * Adds an element to the end of the list. This will increase the - * list's capacity if necessary. - * - * @param value the value to add - */ - public void add(int value) { - growIfNeeded(); - values[size++] = value; - } - - /** - * Inserts element into specified index, moving elements at and above - * that index up one. May not be used to insert at an index beyond the - * current size (that is, insertion as a last element is legal but - * no further). - * - * @param n {@code >= 0, <=size();} index of where to insert - * @param value value to insert - */ - public void insert(int n, int value) { - if (n > size) { - throw new IndexOutOfBoundsException("n > size()"); - } else if(n < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - - growIfNeeded(); - - System.arraycopy (values, n, values, n+1, size - n); - values[n] = value; - size++; - } - - /** - * Removes value from this list. - * - * @param value value to remove - * return {@code true} if the value was removed, {@code false} otherwise - */ - public boolean remove(int value) { - for (int i = 0; i < size; i++) { - if (values[i] == value) { - size--; - System.arraycopy(values, i+1, values, i, size-i); - return true; - } - } - return false; - } - - /** - * Removes an element at a given index, shifting elements at greater - * indicies down one. - * - * @param index index of element to remove - */ - public void removeIndex(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } else if(index < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - size--; - System.arraycopy (values, index + 1, values, index, size - index); - } - - /** - * Increases size of array if needed - */ - private void growIfNeeded() { - if (size == values.length) { - // Resize. - int[] newArray = new int[size * 3 / 2 + 10]; - System.arraycopy(values, 0, newArray, 0, size); - values = newArray; - } - } - - /** - * Returns the index of the given value, or -1 if the value does not - * appear in the list. - * - * @param value value to find - * @return index of value or -1 - */ - private int indexOf(int value) { - for (int i = 0; i < size; i++) { - if (values[i] == value) { - return i; - } - } - return -1; - } - - /** - * Removes all values from this list. - */ - public void clear() { - values = new int[4]; - size = 0; - } - - - /** - * Returns true if the given value is contained in the list - * - * @param value value to look for - * @return {@code true} if this list contains {@code value}, {@code false} otherwise - */ - public boolean contains(int value) { - return indexOf(value) >= 0; - } - - /** - * Returns an array with a copy of this list's values - * - * @return array with a copy of this list's values - */ - public int[] toArray() { - return Arrays.copyOf(values, size); - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java index 7e3870a20..dbdb37c3b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java @@ -1,10 +1,16 @@ package de.danoeh.antennapod.core.util; import android.net.Uri; +import android.text.TextUtils; import androidx.annotation.NonNull; import android.util.Log; import de.danoeh.antennapod.core.BuildConfig; +import okhttp3.HttpUrl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Provides methods for checking and editing a URL. @@ -78,4 +84,45 @@ public final class URLChecker { return prepareURL(url); } } + + public static boolean containsUrl(List<String> list, String url) { + for (String item : list) { + if (urlEquals(item, url)) { + return true; + } + } + return false; + } + + public static boolean urlEquals(String string1, String string2) { + HttpUrl url1 = HttpUrl.parse(string1); + HttpUrl url2 = HttpUrl.parse(string2); + if (!url1.host().equals(url2.host())) { + return false; + } + List<String> pathSegments1 = normalizePathSegments(url1.pathSegments()); + List<String> pathSegments2 = normalizePathSegments(url2.pathSegments()); + if (!pathSegments1.equals(pathSegments2)) { + return false; + } + if (TextUtils.isEmpty(url1.query())) { + return TextUtils.isEmpty(url2.query()); + } + return url1.query().equals(url2.query()); + } + + /** + * Removes empty segments and converts all to lower case. + * @param input List of path segments + * @return Normalized list of path segments + */ + private static List<String> normalizePathSegments(List<String> input) { + List<String> result = new ArrayList<>(); + for (String string : input) { + if (!TextUtils.isEmpty(string)) { + result.add(string.toLowerCase()); + } + } + return result; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/CompareCompat.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/CompareCompat.java new file mode 100644 index 000000000..c189f2389 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/CompareCompat.java @@ -0,0 +1,29 @@ +package de.danoeh.antennapod.core.util.comparator; + +/** + * Some compare() methods are not available before API 19. + * This class provides fallbacks + */ +public class CompareCompat { + + private CompareCompat() { + // Must not be instantiated + } + + /** + * Compares two {@code long} values. Long.compare() is not available before API 19 + * + * @return 0 if long1 = long2, less than 0 if long1 < long2, + * and greater than 0 if long1 > long2. + */ + public static int compareLong(long long1, long long2) { + //noinspection UseCompareMethod + if (long1 > long2) { + return -1; + } else if (long1 < long2) { + return 1; + } else { + return 0; + } + } +} diff --git a/core/src/main/res/drawable/borderless_button_dark.xml b/core/src/main/res/drawable/borderless_button_dark.xml deleted file mode 100644 index 3a44d81a2..000000000 --- a/core/src/main/res/drawable/borderless_button_dark.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true"> - <shape android:shape="rectangle"> - <solid android:color="@color/selection_background_color_dark" /> - </shape> - </item> - <item android:state_focused="true"> - <shape android:shape="rectangle"> - <solid android:color="@color/selection_background_color_dark" /> - </shape> - </item> - <item> - <shape android:shape="rectangle"> - <solid android:color="@android:color/transparent" /> - </shape> - </item> -</selector>
\ No newline at end of file diff --git a/core/src/main/res/values-eu/strings.xml b/core/src/main/res/values-eu/strings.xml index 13cc6b217..3638e8842 100644 --- a/core/src/main/res/values-eu/strings.xml +++ b/core/src/main/res/values-eu/strings.xml @@ -9,7 +9,7 @@ <string name="all_episodes_short_label">Denak</string> <string name="new_episodes_label">Berria</string> <string name="favorite_episodes_label">Gogokoak</string> - <string name="new_label">Berriak</string> + <string name="new_label">Berria</string> <string name="settings_label">Ezarpenak</string> <string name="downloads_label">Deskargak</string> <string name="downloads_running_label">Exekutatzen</string> @@ -283,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Deskargatu osagarria</string> <string name="no_playback_plugin_title">Osagarria instalatu gabe</string> + <string name="no_playback_plugin_or_sonic_msg">Abiadura aldakorreko erreprodukzioak funtziona dezan, Sonic multimedia-erreproduzitzailea gehitzea gomendatzen dugu.</string> <string name="set_playback_speed_label">Erreprodukzio abiadurak</string> <string name="enable_sonic">Gaitu Sonic</string> <!--Empty list labels--> @@ -727,6 +728,7 @@ <string name="notification_channel_error_description">Zerbait gaizki atera den erakusten du, adibidez, gpodder deskargak edo sinkronizazioak huts egiten duen.</string> <string name="import_bad_file">Fitxategi baliogabe/ustela</string> <!--Widget settings--> + <string name="widget_settings">Widget ezarpenak</string> <string name="widget_create_button">widget-a sortu</string> <string name="widget_opacity">Opakutasuna</string> </resources> diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml index c90ea3c91..4418289e9 100644 --- a/core/src/main/res/values-fr/strings.xml +++ b/core/src/main/res/values-fr/strings.xml @@ -283,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Télécharger une extension</string> <string name="no_playback_plugin_title">Extension non installée</string> + <string name="no_playback_plugin_or_sonic_msg">Pour pouvoir changer la vitesse de lecture il est recommandé d\'activer le lecteur interne Sonic.</string> <string name="set_playback_speed_label">Vitesses de lecture</string> <string name="enable_sonic">Activer Sonic</string> <!--Empty list labels--> @@ -724,6 +725,7 @@ <string name="notification_channel_error_description">S\'affiche en cas de problème. Par exemple, un téléchargement ou une synchronisation qui échoue.</string> <string name="import_bad_file">Fichier invalide/corrompu</string> <!--Widget settings--> + <string name="widget_settings">Préférences des widget</string> <string name="widget_create_button">Créer un widget</string> <string name="widget_opacity">Opacité</string> </resources> diff --git a/core/src/main/res/values-gl-rES/strings.xml b/core/src/main/res/values-gl-rES/strings.xml index 411d79430..4a0131a20 100644 --- a/core/src/main/res/values-gl-rES/strings.xml +++ b/core/src/main/res/values-gl-rES/strings.xml @@ -283,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Descargar engadido</string> <string name="no_playback_plugin_title">Engadido non instalado</string> + <string name="no_playback_plugin_or_sonic_msg">Recomendamos activar o reprodutor Sonic incluído para que funcione ben a velocidade variable de reprodución.</string> <string name="set_playback_speed_label">Velocidade de reproducións</string> <string name="enable_sonic">Habilitar Sonic</string> <!--Empty list labels--> @@ -723,6 +724,7 @@ <string name="notification_channel_error_description">Mostrado si algo falla, por exemplo si a descarga ou a sincronización con gpodder fallan.</string> <string name="import_bad_file">Ficheiro non válido/corrupto</string> <!--Widget settings--> + <string name="widget_settings">Axustes do Widget</string> <string name="widget_create_button">Crear widget</string> <string name="widget_opacity">Opacidade</string> </resources> diff --git a/core/src/main/res/values-it/strings.xml b/core/src/main/res/values-it/strings.xml index 111e9932a..384368d35 100644 --- a/core/src/main/res/values-it/strings.xml +++ b/core/src/main/res/values-it/strings.xml @@ -283,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Scarica plugin</string> <string name="no_playback_plugin_title">Plugin non installato</string> + <string name="no_playback_plugin_or_sonic_msg">Per la velocità di riproduzione variabile, raccomandiamo di usare il riproduttore Sonic integrato.</string> <string name="set_playback_speed_label">Velocità di riproduzione</string> <string name="enable_sonic">Abilita Sonic</string> <!--Empty list labels--> @@ -724,6 +725,7 @@ <string name="notification_channel_error_description">Mostrato se qualcosa è andato storto, per esempio se fallisce il download o la sincronizzazione di gpodder.</string> <string name="import_bad_file">File non valido/corrotto</string> <!--Widget settings--> + <string name="widget_settings">Impostazioni widget</string> <string name="widget_create_button">Crea widget</string> <string name="widget_opacity">Opacità</string> </resources> diff --git a/core/src/main/res/values-iw-rIL/strings.xml b/core/src/main/res/values-iw-rIL/strings.xml index dde0220dc..170a9c0a9 100644 --- a/core/src/main/res/values-iw-rIL/strings.xml +++ b/core/src/main/res/values-iw-rIL/strings.xml @@ -301,6 +301,7 @@ <!--Variable Speed--> <string name="download_plugin_label">הורדת תוסף</string> <string name="no_playback_plugin_title">תוסף לא מותקן</string> + <string name="no_playback_plugin_or_sonic_msg">כדי שתהיה לך אפשרות להשתמש במהירות נגינה משתנה אנו ממליצים להפעיל את נגן המדיה המובנה Sonic.</string> <string name="set_playback_speed_label">מהירויות ניגון</string> <string name="enable_sonic">הפעלת Sonic</string> <!--Empty list labels--> @@ -748,6 +749,7 @@ <string name="notification_channel_error_description">מופיעות אם משהו משתבש, למשל אם הורדה או סנכרון מול gpodder נכשלים.</string> <string name="import_bad_file">קובץ לא תקין/פגום</string> <!--Widget settings--> + <string name="widget_settings">הגדרות וידג׳ט</string> <string name="widget_create_button">יצירת וידג׳</string> <string name="widget_opacity">אטימות</string> </resources> diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml index 8f0933564..8afea51e0 100644 --- a/core/src/main/res/values-ja/strings.xml +++ b/core/src/main/res/values-ja/strings.xml @@ -274,6 +274,7 @@ <!--Variable Speed--> <string name="download_plugin_label">プラグインをダウンロード</string> <string name="no_playback_plugin_title">プラグイン はインストールされていません</string> + <string name="no_playback_plugin_or_sonic_msg">可変速再生を機能させるには、内蔵のSonicメディアプレーヤーを有効にすることをお勧めします。</string> <string name="set_playback_speed_label">再生速度</string> <string name="enable_sonic">Sonic を有効にする</string> <!--Empty list labels--> @@ -712,6 +713,7 @@ <string name="notification_channel_error_description">ダウンロードや gpodder の同期に失敗した場合など、何か問題が発生した場合に表示されます。</string> <string name="import_bad_file">無効/破損ファイル</string> <!--Widget settings--> + <string name="widget_settings">ウィジェット設定</string> <string name="widget_create_button">ウィジェットを作成</string> <string name="widget_opacity">透明度</string> </resources> diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml index caa2c9ce7..338f4c193 100644 --- a/core/src/main/res/values-nl/strings.xml +++ b/core/src/main/res/values-nl/strings.xml @@ -9,21 +9,21 @@ <string name="all_episodes_short_label">Alle</string> <string name="new_episodes_label">Nieuw</string> <string name="favorite_episodes_label">Favorieten</string> - <string name="new_label">Nieuw</string> + <string name="new_label">Nieuwe</string> <string name="settings_label">Instellingen</string> <string name="downloads_label">Downloads</string> <string name="downloads_running_label">Bezig</string> <string name="downloads_completed_label">Voltooid</string> <string name="downloads_log_label">Logboek</string> <string name="subscriptions_label">Abonnementen</string> - <string name="subscriptions_list_label">Lijst met abonnementen</string> + <string name="subscriptions_list_label">Abonnementenlijst</string> <string name="cancel_download_label">Download\nafbreken</string> <string name="playback_history_label">Afspeelgeschiedenis</string> <string name="gpodnet_main_label">gpodder.net</string> <string name="gpodnet_summary">Synchroniseren met andere apparaten</string> <string name="gpodnet_auth_label">Inloggen op gpodder.net</string> <string name="episode_cache_full_title">Afleveringscache is vol</string> - <string name="episode_cache_full_message">Het maximum aantal gecachete afleveringen is bereikt. Je kunt het maximum verhogen in de instellingen.</string> + <string name="episode_cache_full_message">Het maximum aantal gecachete afleveringen is bereikt. Je kunt dit aantal verhogen in de instellingen.</string> <!--Statistics fragment--> <string name="total_time_listened_to_podcasts">Totale duur van afgespeelde podcasts:</string> <string name="statistics_details_dialog">%1$d van %2$d afleveringen gestart.\n\n%3$s van %4$s afgespeeld.</string> @@ -31,13 +31,15 @@ <string name="statistics_mode_normal">Bereken de daadwerkelijke speeltijd. Tweemaal afspelen wordt twee keer gerekend; gemarkeerd als \'afgespeeld\' wordt niet meegeteld.</string> <string name="statistics_mode_count_all">Duur van de podcasts optellen die als \'afgespeeld\' gemarkeerd zijn</string> <string name="statistics_speed_not_counted">Let op: er wordt geen rekening gehouden met de afspeelsnelheid.</string> + <string name="statistics_reset_data">Statistieken op nul zetten</string> + <string name="statistics_reset_data_msg">Dit wist alle luistertijden van alle afleveringen. Weet je zeker dat je wilt doorgaan?</string> <!--Main activity--> <string name="drawer_open">Menu openen</string> <string name="drawer_close">Menu sluiten</string> <string name="drawer_preferences">Menuvoorkeuren</string> - <string name="drawer_feed_order_unplayed_episodes">Op aantal afleveringen</string> + <string name="drawer_feed_order_unplayed_episodes">Sorteren</string> <string name="drawer_feed_order_alphabetical">Op alfabetische volgorde</string> - <string name="drawer_feed_order_last_update">Op datum van publicatie</string> + <string name="drawer_feed_order_last_update">Op verschijningsdatum</string> <string name="drawer_feed_order_most_played">Op aantal afgespeelde afleveringen</string> <string name="drawer_feed_counter_new_unplayed">Aantal nieuwe plus onbeluisterde afleveringen</string> <string name="drawer_feed_counter_new">Aantal nieuwe afleveringen</string> @@ -48,7 +50,7 @@ <string name="open_in_browser_label">Openen in browser</string> <string name="copy_url_label">URL kopiëren</string> <string name="share_url_label">URL delen</string> - <string name="copied_url_msg">URL is gekopieerd naar het klembord</string> + <string name="copied_url_msg">De url is gekopieerd naar het klembord</string> <string name="go_to_position_label">Ga naar deze positie</string> <!--Playback history--> <string name="clear_history_label">Geschiedenis wissen</string> @@ -64,9 +66,9 @@ <string name="cover_label">Beeld</string> <string name="error_label">Fout</string> <string name="error_msg_prefix">Er is een fout opgetreden:</string> - <string name="needs_storage_permission">Machtiging voor gebruik van opslag voor deze functie vereist</string> + <string name="needs_storage_permission">Machtiging vereist voor gebruik van opslag</string> <string name="refresh_label">Verversen</string> - <string name="external_storage_error_msg">Geen externe opslag beschikbaar. Zorg ervoor dat de externe opslag is aangekoppeld, zodat de app goed kan werken.</string> + <string name="external_storage_error_msg">Geen externe opslag beschikbaar. Zorg ervoor dat de externe opslag is aangekoppeld zodat de app goed kan werken.</string> <string name="chapters_label">Hoofdstukken</string> <string name="chapter_duration">Duur: %1$s</string> <string name="description_label">Omschrijving</string> @@ -77,7 +79,7 @@ <string name="retry_label">Opnieuw</string> <string name="auto_download_label">Meenemen bij automatisch downloaden</string> <string name="auto_download_apply_to_items_title">Toepassen op vorige afleveringen</string> - <string name="auto_download_apply_to_items_message">De instelling voor <i>Automatisch downloaden</i> wordt automatisch toegepast op toekomstige afleveringen.\nWil je het ook toepassen op eerder gepubliceerde afleveringen?</string> + <string name="auto_download_apply_to_items_message">De instelling voor <i>Automatisch downloaden</i> wordt automatisch toegepast op toekomstige afleveringen.\nWil je deze ook toepassen op eerder gepubliceerde afleveringen?</string> <string name="auto_delete_label">Aflevering automatisch verwijderen</string> <string name="parallel_downloads_suffix">\u0020gelijktijdige downloads</string> <string name="feed_auto_download_global">Standaardinstelling</string> @@ -85,22 +87,22 @@ <string name="feed_auto_download_never">Nooit</string> <string name="send_label">Versturen...</string> <string name="episode_cleanup_never">Nooit</string> - <string name="episode_cleanup_queue_removal">Indien niet in de wachtrij</string> + <string name="episode_cleanup_queue_removal">Indien niet in wachtrij</string> <string name="episode_cleanup_after_listening">Als aflevering volledig is afgespeeld</string> <plurals name="episode_cleanup_hours_after_listening"> <item quantity="one">1 uur na afronden</item> <item quantity="other">%d uur na afronden</item> </plurals> <plurals name="episode_cleanup_days_after_listening"> - <item quantity="one">1 dag nadat de aflevering is gemarkeerd als afgespeeld</item> - <item quantity="other">%d dagen nadat de aflevering is gemarkeerd als afgespeeld</item> + <item quantity="one">1 dag na afronden</item> + <item quantity="other">%d dagen na afronden</item> </plurals> <string name="num_selected_label">%d geselecteerd</string> <!--'Add Feed' Activity labels--> - <string name="feedurl_label">Feed-URL</string> + <string name="feedurl_label">Feed-url</string> <string name="etxtFeedurlHint">www.voorbeeld.nl/feed</string> - <string name="txtvfeedurl_label">Podcast toevoegen via URL</string> - <string name="browse_gpoddernet_label">Blader door gpodder.net</string> + <string name="txtvfeedurl_label">Podcast toevoegen middels url</string> + <string name="browse_gpoddernet_label">Verken gpodder.net</string> <string name="discover">Ontdekken</string> <string name="discover_more">meer »</string> <!--Actions on feeds--> @@ -108,23 +110,24 @@ <string name="mark_all_read_msg">Alle afleveringen zijn gemarkeerd als afgespeeld</string> <string name="mark_all_read_confirmation_msg">Bevestig dat je alle afleveringen wilt markeren als afgespeeld.</string> <string name="mark_all_read_feed_confirmation_msg">Bevestig dat je alle afleveringen van deze podcast wilt markeren als afgespeeld.</string> - <string name="remove_all_new_flags_label">Alle labels met \"nieuw\" verwijderen</string> - <string name="removed_all_new_flags_msg">Alle labels met \"nieuw\" zijn verwijderd</string> - <string name="remove_all_new_flags_confirmation_msg">Bevestig dat je alle labels met \"nieuw\" wilt verwijderen bij alle afleveringen.</string> + <string name="remove_all_new_flags_label">Alle \'nieuw\'-labels verwijderen</string> + <string name="removed_all_new_flags_msg">Alle \'nieuw\'-labels zijn verwijderd</string> + <string name="remove_all_new_flags_confirmation_msg">Bevestig dat je alle \'nieuw\'-labels wilt verwijderen bij alle afleveringen.</string> <string name="show_info_label">Informatie tonen</string> - <string name="show_feed_settings_label">Podcast-instellingen tonen</string> - <string name="feed_info_label">Podcast-informatie</string> - <string name="feed_settings_label">Podcast-instellingen</string> + <string name="show_feed_settings_label">Podcastinstellingen tonen</string> + <string name="feed_info_label">Podcastinformatie</string> + <string name="feed_settings_label">Podcastinstellingen</string> <string name="rename_feed_label">Podcastnaam wijzigen</string> <string name="remove_feed_label">Podcast verwijderen</string> <string name="share_label">Delen...</string> - <string name="share_link_label">URL aflevering</string> - <string name="share_link_with_position_label">URL aflevering, met tijdstip</string> + <string name="share_link_label">URL van aflevering delen</string> + <string name="share_link_with_position_label">URL van aflevering, incl. afspeelpositie, delen</string> <string name="share_file_label">Bestand delen</string> - <string name="share_feed_url_label">URL podcast</string> - <string name="share_item_url_label">URL mediabestand</string> - <string name="share_item_url_with_position_label">URL mediabestand delen, met tijdstip</string> - <string name="feed_delete_confirmation_msg">Bevestig dat je de podcast \"%1$s\" en ALLE (ook gedownloade) bijbehorende afleveringen wilt verwijderen.</string> + <string name="share_website_url_label">Website-url delen</string> + <string name="share_feed_url_label">Feed-url delen</string> + <string name="share_item_url_label">URL van mediabestand delen</string> + <string name="share_item_url_with_position_label">URL van mediabestand, incl. afspeelpositie, delen</string> + <string name="feed_delete_confirmation_msg">Bevestig dat je de podcast \'%1$s\' en ALLE bijbehorende (gedownloade) afleveringen wilt verwijderen.</string> <string name="feed_remover_msg">Podcast verwijderen</string> <string name="load_complete_feed">Gehele podcast verversen</string> <string name="batch_edit">Bulkbewerking</string> @@ -158,8 +161,8 @@ <item quantity="one">%d aflevering verwijderd.</item> <item quantity="other">%d afleveringen verwijderd.</item> </plurals> - <string name="remove_new_flag_label">Label met \"nieuw\" verwijderen</string> - <string name="removed_new_flag_label">Label met \"nieuw\" is verwijderd</string> + <string name="remove_new_flag_label">\'Nieuw\'-label verwijderen</string> + <string name="removed_new_flag_label">\'Nieuw\'-label is verwijderd</string> <string name="mark_read_label">Markeren als afgespeeld</string> <string name="marked_as_read_label">Gemarkeerd als afgespeeld</string> <plurals name="marked_read_batch_label"> @@ -174,27 +177,27 @@ <string name="add_to_queue_label">Toevoegen aan wachtrij</string> <string name="added_to_queue_label">Toegevoegd aan wachtrij</string> <plurals name="added_to_queue_batch_label"> - <item quantity="one">%d aflevering toegevoegd aan wachtrij.</item> - <item quantity="other">%d afleveringen toegevoegd aan wachtrij.</item> + <item quantity="one">%d aflevering toegevoegd aan de wachtrij.</item> + <item quantity="other">%d afleveringen toegevoegd aan de wachtrij.</item> </plurals> <string name="remove_from_queue_label">Verwijderen uit wachtrij</string> <plurals name="removed_from_queue_batch_label"> - <item quantity="one">%d aflevering verwijderd uit wachtrij.</item> - <item quantity="other">%d afleveringen verwijderd uit wachtrij.</item> + <item quantity="one">%d aflevering verwijderd uit de wachtrij.</item> + <item quantity="other">%d afleveringen verwijderd uit de wachtrij.</item> </plurals> <string name="add_to_favorite_label">Toevoegen aan favorieten</string> <string name="added_to_favorites">Toegevoegd aan favorieten</string> <string name="remove_from_favorite_label">Verwijderen uit favorieten</string> <string name="removed_from_favorites">Verwijderd uit favorieten</string> - <string name="visit_website_label">Website bezoeken</string> + <string name="visit_website_label">Website openen</string> <string name="skip_episode_label">Aflevering overslaan</string> - <string name="activate_auto_download">Automatisch downloaden activeren</string> - <string name="deactivate_auto_download">Automatisch downloaden de-activeren</string> + <string name="activate_auto_download">Automatisch downloaden inschakelen</string> + <string name="deactivate_auto_download">Automatisch downloaden uitschakelen</string> <string name="reset_position">Afspeelpositie herstellen</string> - <string name="removed_item">Aflevering verwijderd</string> + <string name="removed_item">Item verwijderd</string> <!--Download messages and labels--> <string name="download_successful">voltooid</string> - <string name="download_pending">Download staat in wachtrij</string> + <string name="download_pending">Download staat in de wachtrij</string> <string name="download_running">Bezig met downloaden</string> <string name="download_error_details">Details</string> <string name="download_error_details_message">%1$s \n\nURL van bestand:\n%2$s</string> @@ -212,9 +215,9 @@ <string name="download_canceled_msg">Download afgebroken</string> <string name="download_canceled_autodownload_enabled_msg">Download afgebroken\n<i>Automatisch downloaden</i> uitgeschakeld voor deze aflevering</string> <string name="download_report_title">Downloads afgerond, maar met fout(en)</string> - <string name="download_report_content_title">Downloadrapport</string> - <string name="download_error_malformed_url">Onjuist opgemaakte URL</string> - <string name="download_error_io_error">Invoer-/uitvoerfout</string> + <string name="download_report_content_title">Downloadlogboek</string> + <string name="download_error_malformed_url">Onjuist opgemaakte url</string> + <string name="download_error_io_error">In-/Uitvoerfout</string> <string name="download_error_request_error">Opvraagfout</string> <string name="download_error_db_access">Fout bij databanktoegang</string> <plurals name="downloads_left"> @@ -223,7 +226,7 @@ </plurals> <string name="downloads_processing">Bezig met verwerken van downloads</string> <string name="download_notification_title">Bezig met downloaden van podcastgegevens</string> - <string name="download_report_content">%1$d downloads voltooid, %2$d mislukt</string> + <string name="download_report_content">%1$d downloads voltooid; %2$d mislukt</string> <string name="download_log_title_unknown">Onbekende titel</string> <string name="download_type_feed">Feed</string> <string name="download_type_media">Mediabestand</string> @@ -232,7 +235,7 @@ <string name="authentication_notification_title">Authenticatie vereist</string> <string name="authentication_notification_msg">De opgevraagde bron vereist een gebruikersnaam en wachtwoord</string> <string name="confirm_mobile_download_dialog_title">Bevestig downloaden via mobiel internet</string> - <string name="confirm_mobile_download_dialog_message_not_in_queue">Downloaden via mobiel internet is uitgeschakeld in de instellingen.\n\nJe kunt er voor kiezen om de aflevering alleen toe te voegen aan de wachtrij of om downloaden via mobiele internet tijdelijk toe te staan.\n<small>Je keuze wordt 10 minuten onthouden.</small></string> + <string name="confirm_mobile_download_dialog_message_not_in_queue">Downloaden via mobiel internet is uitgeschakeld in de instellingen.\n\nJe kunt er voor kiezen om de aflevering alleen toe te voegen aan de wachtrij of om downloaden via mobiel internet tijdelijk toe te staan.\n<small>Je keuze wordt 10 minuten onthouden.</small></string> <string name="confirm_mobile_download_dialog_message">Downloaden via mobiel internet is uitgeschakeld in de instellingen.\n\nWil je dit tijdelijk toestaan?\n<small>Je keuze wordt 10 minuten onthouden.</small></string> <string name="confirm_mobile_streaming_notification_title">Bevestig streamen via mobiel internet</string> <string name="confirm_mobile_streaming_notification_message">Streamen via mobiel internet is uitgeschakeld in de instellingen. Druk om tóch te streamen.</string> @@ -241,7 +244,7 @@ <string name="confirm_mobile_download_dialog_enable_temporarily">Tijdelijk toestaan</string> <!--Mediaplayer messages--> <string name="player_error_msg">Fout!</string> - <string name="player_stopped_msg">Geen media aan het afspelen</string> + <string name="player_stopped_msg">Er wordt geen media afgespeeld</string> <string name="player_preparing_msg">Bezig met voorbereiden</string> <string name="player_ready_msg">Klaar</string> <string name="player_seeking_msg">Bezig met zoeken</string> @@ -249,15 +252,17 @@ <string name="playback_error_unsupported">Niet-ondersteund bestandstype</string> <string name="playback_error_timeout">Operatie verlopen</string> <string name="playback_error_unknown">Onbekende fout</string> - <string name="no_media_playing_label">Geen media aan het afspelen</string> + <string name="no_media_playing_label">Er wordt geen media afgespeeld</string> <string name="player_buffering_msg">Bezig met bufferen</string> - <string name="player_go_to_picture_in_picture">Picture-in-picture-modus</string> + <string name="player_go_to_picture_in_picture">Beeld-in-beeldmodus</string> <string name="unknown_media_key">AntennaPod - onbekende mediatoets: %1$d</string> <!--Queue operations--> <string name="lock_queue">Wachtrij vergrendelen</string> <string name="unlock_queue">Wachtrij ontgrendelen</string> <string name="queue_locked">Wachtrij vergrendeld</string> <string name="queue_unlocked">Wachtrij ontgrendeld</string> + <string name="queue_lock_warning">Als je de wachtrij vergrendelt, dan kun je afleveringen niet meer herordenen of wegvegen.</string> + <string name="checkbox_do_not_show_again">Niet opnieuw tonen</string> <string name="clear_queue_label">Wachtrij leegmaken</string> <string name="undo">Ongedaan maken</string> <string name="move_to_top_label">Naar boven verplaatsen</string> @@ -278,18 +283,19 @@ <!--Variable Speed--> <string name="download_plugin_label">Plug-in downloaden</string> <string name="no_playback_plugin_title">Plug-in niet geïnstalleerd</string> + <string name="no_playback_plugin_or_sonic_msg">Als je variabele afspeelsnelheden aan de praat wilt krijgen, dan raden we aan om de ingebouwde Sonic-mediaspeler te gebruiken.</string> <string name="set_playback_speed_label">Afspeelsnelheden</string> <string name="enable_sonic">Sonic inschakelen</string> <!--Empty list labels--> <string name="no_items_header_label">Geen afleveringen in wachtrij</string> <string name="no_items_label">Voeg een aflevering toe door deze te downloaden of houd een aflevering lang ingedrukt en kies \'Toevoegen aan wachtrij\'.</string> - <string name="no_shownotes_label">Deze aflevering bevat geen shownotities.</string> - <string name="no_run_downloads_head_label">Geen actieve downloads</string> + <string name="no_shownotes_label">Deze aflevering bevat geen aantekeningen.</string> + <string name="no_run_downloads_head_label">Er wordt niks gedownload</string> <string name="no_run_downloads_label">Je kunt afleveringen downloaden op het podcastinformatiescherm.</string> <string name="no_comp_downloads_head_label">Geen gedownloade afleveringen</string> <string name="no_comp_downloads_label">Je kunt afleveringen downloaden op het podcastinformatiescherm.</string> - <string name="no_log_downloads_head_label">Geen download-logbestand</string> - <string name="no_log_downloads_label">Download-logbestanden verschijnen hier.</string> + <string name="no_log_downloads_head_label">Geen downloadlogboek</string> + <string name="no_log_downloads_label">Downloadlogboeken verschijnen hier.</string> <string name="no_history_head_label">Geen geschiedenis</string> <string name="no_history_label">Als je een aflevering hebt beluisterd, dan verschijnt deze hier.</string> <string name="no_all_episodes_head_label">Geen afleveringen</string> @@ -300,6 +306,8 @@ <string name="no_fav_episodes_label">Je kunt afleveringen toevoegen aan je favorieten door ze lang ingedrukt te houden.</string> <string name="no_chapters_head_label">Geen hoofdstukken</string> <string name="no_chapters_label">Deze aflevering bevat geen hoofdstukken.</string> + <string name="no_subscriptions_head_label">Geen abonnementen</string> + <string name="no_subscriptions_label">Druk op het plus-pictogram om je te abonneren op een podcast.</string> <!--Preferences--> <string name="storage_pref">Opslag</string> <string name="project_pref">Project</string> @@ -318,13 +326,13 @@ <string name="preference_search_clear_history">Geschiedenis wissen</string> <string name="media_player">Mediaspeler</string> <string name="pref_episode_cleanup_title">Automatisch opschonen</string> - <string name="pref_episode_cleanup_summary">Afleveringen die niet op de wachtrij staan én geen favoriet zijn, mogen verwijderd worden als Automatisch downloaden ruimte nodig heeft voor nieuwe afleveringen</string> - <string name="pref_pauseOnDisconnect_sum">Afspelen pauzeren als de koptelefoon wordt losgekoppeld of de Bluetooth-verbinding wordt verbroken</string> - <string name="pref_unpauseOnHeadsetReconnect_sum">Afspelen hervatten als de koptelefoon opnieuw wordt aangesloten</string> - <string name="pref_unpauseOnBluetoothReconnect_sum">Afspelen hervatten als de Bluetooth-verbinding hervat wordt</string> - <string name="pref_hardwareForwardButtonSkips_title">\'Volgende\' voor overslaan</string> - <string name="pref_hardwareForwardButtonSkips_sum">Als je op de Vooruit-knop van een via Bluetooth verbonden apparaat drukt, dan wordt de volgende aflevering geladen i.p.v. doorgespoeld</string> - <string name="pref_hardwarePreviousButtonRestarts_title">\'Vorige\' voor opnieuw afspelen</string> + <string name="pref_episode_cleanup_summary">Afleveringen die niet in de wachtrij staan én geen favoriet zijn, mogen verwijderd worden als \'Automatisch downloaden\' ruimte nodig heeft voor nieuwe afleveringen</string> + <string name="pref_pauseOnDisconnect_sum">Afspelen pauzeren als de koptelefoon wordt losgekoppeld of de Bluetooth-verbinding verbroken</string> + <string name="pref_unpauseOnHeadsetReconnect_sum">Afspelen hervatten als de koptelefoon weer wordt aangesloten</string> + <string name="pref_unpauseOnBluetoothReconnect_sum">Afspelen hervatten als de Bluetooth-verbinding hervat</string> + <string name="pref_hardwareForwardButtonSkips_title">\'Vooruit\' gebruiken voor overslaan</string> + <string name="pref_hardwareForwardButtonSkips_sum">Als je op de vooruitknop van een via Bluetooth verbonden apparaat drukt, wordt de volgende aflevering geladen i.p.v. doorgespoeld</string> + <string name="pref_hardwarePreviousButtonRestarts_title">\'Vorige\' gebruiken voor opnieuw afspelen</string> <string name="pref_hardwarePreviousButtonRestarts_sum">Aflevering afspelen vanaf het begin i.p.v. terugspoelen als er op een fysieke \'vorige\'-knop wordt gedrukt</string> <string name="pref_followQueue_sum">Volgende item in de wachtrij afspelen als de aflevering voltooid is</string> <string name="pref_auto_delete_sum">Afleveringen verwijderen als ze zijn afgespeeld</string> @@ -349,6 +357,8 @@ <string name="pref_pauseOnHeadsetDisconnect_title">Loskoppelen van hoofdtelefoon</string> <string name="pref_unpauseOnHeadsetReconnect_title">Opnieuw aansluiten van hoofdtelefoon</string> <string name="pref_unpauseOnBluetoothReconnect_title">Opnieuw verbinden met Bluetooth</string> + <string name="pref_stream_over_download_title">Voorkeur geven aan streaming</string> + <string name="pref_stream_over_download_sum">Toon een stream- i.p.v. downloadknop in lijsten.</string> <string name="pref_mobileUpdate_title">Bijwerken via mobiel internet</string> <string name="pref_mobileUpdate_sum">Kies wat je wilt toestaan via mobiel internet</string> <string name="pref_mobileUpdate_refresh">Feedverversing</string> @@ -369,11 +379,16 @@ <string name="pref_automatic_download_sum">Stel het automatisch downloaden van afleveringen in.</string> <string name="pref_autodl_wifi_filter_title">Wi-Fi-filter inschakelen</string> <string name="pref_autodl_wifi_filter_sum">Automatisch downloaden alleen toestaan via gekozen Wi-Fi-netwerken.</string> + <string name="autodl_wifi_filter_permission_title">Machtiging vereist</string> + <string name="autodl_wifi_filter_permission_message">Locatietoegang is vereist om het Wi-Fi-filter te kunnen gebruiken. Druk om te machtigen.</string> <string name="pref_automatic_download_on_battery_title">Downloaden als het apparaat niet wordt opgeladen</string> <string name="pref_automatic_download_on_battery_sum">Downloaden toestaan als het apparaat niet wordt opgeladen</string> <string name="pref_parallel_downloads_title">Gelijktijdige downloads</string> <string name="pref_episode_cache_title">Afleveringscache</string> <string name="pref_episode_cache_summary">Het totaal aantal gedownloade afleveringen dat moet worden opgeslagen op het apparaat. Automatische downloads worden onderbroken als dit aantal wordt bereikt.</string> + <string name="pref_episode_cover_title">Omslag van aflevering gebruiken</string> + <string name="pref_episode_cover_summary">Gebruik de bij de aflevering behorende omslag (indien beschikbaar). Als je dit niet inschakelt, dan wordt altijd de omslag van de podcast gebruikt.</string> + <string name="pref_theme_title_use_system">Systeemthema gebruiken</string> <string name="pref_theme_title_light">Licht</string> <string name="pref_theme_title_dark">Donker</string> <string name="pref_theme_title_trueblack">Zwart (geschikt voor AMOLED)</string> @@ -398,6 +413,7 @@ <string name="pref_gpodnet_notifications_sum">Deze instelling is niet van toepassing op inlogfouten.</string> <string name="pref_playback_speed_title">Afspeelsnelheden</string> <string name="pref_playback_speed_sum">Pas de beschikbare snelheden aan voor de variabele audio-afspeelsnelheid</string> + <string name="pref_feed_playback_speed_sum">De te gebruiken snelheid bij het afspelen van afleveringen in deze feed</string> <string name="pref_playback_time_respects_speed_title">Media-informatie aanpassen aan afspeelsnelheid</string> <string name="pref_playback_time_respects_speed_sum">Getoonde positie en duur worden aangepast aan de afspeelsnelheid</string> <string name="pref_fast_forward">Snelheid van vooruitspoelen</string> @@ -419,14 +435,25 @@ <string name="pref_showDownloadReport_title">Downloadrapportage tonen</string> <string name="pref_showDownloadReport_sum">Genereer een rapport met foutdetails als downloads mislukken.</string> <string name="pref_expand_notify_unsupport_toast">Android-versies lager dan 4.1 ondersteunen geen knoppen op meldingen.</string> + <string name="pref_enqueue_location_title">Wachtrijlocatie</string> + <string name="pref_enqueue_location_sum">Afleveringen toevoegen aan: %1$s</string> + <string name="enqueue_location_back">Einde</string> + <string name="enqueue_location_front">Begin</string> + <string name="enqueue_location_after_current">Na huidige aflevering</string> <string name="pref_smart_mark_as_played_disabled">Uitgeschakeld</string> <string name="pref_image_cache_size_title">Grootte van afbeeldingscache</string> <string name="pref_image_cache_size_sum">Pas de grootte aan van het cachegeheugen voor afbeeldingen.</string> + <string name="view_mailing_list">Mailinglijst bekijken</string> + <string name="bug_report_title">Bug melden</string> + <string name="open_bug_tracker">Bugtracker openen</string> + <string name="copy_to_clipboard">Kopiëren naar klembord</string> + <string name="copied_to_clipboard">Gekopieerd naar het klembord</string> <string name="experimental_pref">Experimenteel</string> <string name="pref_media_player_message">Kies welke mediaspeler gebruikt moet worden voor het afspelen van bestanden</string> <string name="pref_current_value">Huidige instelling: %1$s</string> <string name="pref_proxy_title">Proxy</string> <string name="pref_proxy_sum">Netwerkproxy instellen</string> + <string name="pref_faq">Veelgestelde vragen</string> <string name="pref_no_browser_found">Geen browser aangetroffen.</string> <string name="pref_cast_title">Chromecast-ondersteuning</string> <string name="pref_cast_message_play_flavor">Ondersteuning activeren voor draadloos afspelen via Cast-apparaten (zoals Chromecast, luidsprekers of Android TV)</string> @@ -454,6 +481,14 @@ <string name="pref_delete_removes_from_queue_sum">Verwijder een aflevering uit de wachtrij als de aflevering in kwestie is verwijderd.</string> <!--About screen--> <string name="about_pref">Over AntennaPod</string> + <string name="antennapod_version">AntennaPod-versie</string> + <string name="developers">Ontwikkelaars</string> + <string name="developers_summary">Iedereen kan helpen AntennaPod te verbeteren</string> + <string name="translators">Vertalers</string> + <string name="translators_summary">De vertalingen zijn gedaan door AntennaPod-gebruikers op Transifex</string> + <string name="privacy_policy">Privacybeleid</string> + <string name="licenses">Licenties</string> + <string name="licenses_summary">AntennaPod maakt gebruik van andere geweldige software</string> <!--Search--> <string name="search_hint">Zoeken naar afleveringen</string> <string name="found_in_shownotes_label">Gevonden in de shownotities</string> @@ -522,10 +557,10 @@ <string name="username_label">Gebruikersnaam</string> <string name="password_label">Wachtwoord</string> <string name="gpodnetauth_device_title">Apparaatkeuze</string> - <string name="gpodnetauth_device_descr">Creëer een nieuw apparaat voor je gpodder.net-account of kies een bestaand:</string> + <string name="gpodnetauth_device_descr">Voeg een nieuw apparaat toe aan je gpodder.net-account of kies een bestaand:</string> <string name="gpodnetauth_device_deviceID">Apparaat-ID:\u0020</string> <string name="gpodnetauth_device_caption">Omschrijving</string> - <string name="gpodnetauth_device_butCreateNewDevice">Nieuw apparaat creëren</string> + <string name="gpodnetauth_device_butCreateNewDevice">Apparaat toevoegen</string> <string name="gpodnetauth_device_chooseExistingDevice">Bestaand apparaat kiezen:</string> <string name="gpodnetauth_device_errorEmpty">Apparaat-ID mag niet blanco zijn</string> <string name="gpodnetauth_device_errorAlreadyUsed">Apparaat-ID wordt al gebruikt</string> @@ -544,16 +579,16 @@ <string name="gpodnetsync_username_characters_error">Gebruikersnamen mogen alleen letters, cijfers en (onderliggende) streepjes bevatten.</string> <!--Directory chooser--> <string name="selected_folder_label">Gekozen map:</string> - <string name="create_folder_label">Map creëren</string> + <string name="create_folder_label">Map aanmaken</string> <string name="choose_data_directory">Kies de gegevensmap</string> - <string name="choose_data_directory_message">Kies de hoofdmap voor je gegevens. AntennaPod zal de benodigde submappen creëren.</string> + <string name="choose_data_directory_message">Kies de hoofdmap voor je gegevens. AntennaPod zal de benodigde submappen aanmaken.</string> <string name="choose_data_directory_permission_rationale">Toegang tot externe opslag is nodig om de gegevensmap te wijzigen</string> <string name="choose_data_directory_available_space">%1$s van %2$s beschikbaar</string> - <string name="create_folder_msg">Wil je een nieuwe map creëren met de naam \"%1$s\"?</string> - <string name="create_folder_success">Nieuwe map gecreëerd</string> + <string name="create_folder_msg">Wil je een nieuwe map aanmaken met de naam \"%1$s\"?</string> + <string name="create_folder_success">Nieuwe map aangemaakt</string> <string name="create_folder_error_no_write_access">Kan niet schrijven naar map</string> <string name="create_folder_error_already_exists">Map bestaat al</string> - <string name="create_folder_error">Kan map niet creëren</string> + <string name="create_folder_error">Kan map niet aanmaken</string> <string name="folder_does_not_exist_error">\"%1$s\" bestaat niet</string> <string name="folder_not_readable_error">\"%1$s\" kan niet worden uitgelezen</string> <string name="folder_not_writable_error">Kan niet wegschrijven naar \"%1$s\"</string> @@ -567,6 +602,7 @@ <string name="pref_restart_required">AntennaPod moet opnieuw worden gestart om deze wijziging toe te passen.</string> <!--Online feed view--> <string name="subscribe_label">Abonneren</string> + <string name="subscribing_label">Bezig met abonneren...</string> <!--Content descriptions for image buttons--> <string name="rewind_label">Terugspoelen</string> <string name="fast_forward_label">Vooruitspoelen</string> @@ -689,4 +725,7 @@ <string name="notification_channel_error_description">Tonen als er iets is misgegaan, bijvoorbeeld als downloaden of synchroniseren mislukt is.</string> <string name="import_bad_file">Ongeldig/Foutief bestand</string> <!--Widget settings--> + <string name="widget_settings">Widgetinstellingen</string> + <string name="widget_create_button">Widget maken</string> + <string name="widget_opacity">Doorzichtigheid</string> </resources> diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml index d6b227f6c..5530b5a23 100644 --- a/core/src/main/res/values-pt/strings.xml +++ b/core/src/main/res/values-pt/strings.xml @@ -283,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Descarregar extra</string> <string name="no_playback_plugin_title">Extra não instalado</string> + <string name="no_playback_plugin_or_sonic_msg">Para que a velocidade variável de reprodução funcione, recomendamos que ative o Sonic Media Player incorporado na aplicação.</string> <string name="set_playback_speed_label">Velocidades de reprodução</string> <string name="enable_sonic">Ativar Sonic</string> <!--Empty list labels--> @@ -724,6 +725,7 @@ <string name="notification_channel_error_description">Mostrar se ocorrerem erros como, por exemplo, não for possível a descarga.</string> <string name="import_bad_file">Ficheiro inválido/danificado</string> <!--Widget settings--> + <string name="widget_settings">Definições do widget</string> <string name="widget_create_button">Criar widget</string> <string name="widget_opacity">Opacidade</string> </resources> diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml index 5c4753f41..f9054a6ed 100644 --- a/core/src/main/res/values-ru/strings.xml +++ b/core/src/main/res/values-ru/strings.xml @@ -31,6 +31,8 @@ <string name="statistics_mode_normal">Рассчитывать длительность действительного воспроизведения. Повторное воспроизведение засчитывается, а отметка о прослушивании — нет</string> <string name="statistics_mode_count_all">Прибавлять также все подкасты, отмеченные как прослушанные</string> <string name="statistics_speed_not_counted">Замечание: Скорость воспроизведения не учитывается никогда.</string> + <string name="statistics_reset_data">Сброс статистики</string> + <string name="statistics_reset_data_msg">Подтвердите своё намерение стереть всю историю прослушанных выпусков.</string> <!--Main activity--> <string name="drawer_open">Открыть меню</string> <string name="drawer_close">Закрыть меню</string> @@ -125,6 +127,7 @@ <string name="share_link_label">Поделиться ссылкой на выпуск</string> <string name="share_link_with_position_label">Поделиться ссылкой на выпуск с отметкой времени</string> <string name="share_file_label">Поделиться файлом</string> + <string name="share_website_url_label">Поделиться ссылкой на сайт</string> <string name="share_feed_url_label">Поделиться ссылкой на канал</string> <string name="share_item_url_label">Поделиться ссылкой на файл</string> <string name="share_item_url_with_position_label">Поделиться ссылкой на файл с отметкой времени</string> @@ -279,6 +282,8 @@ URL файла: <string name="unlock_queue">Разблокировать очередь</string> <string name="queue_locked">Очередь заблокирована</string> <string name="queue_unlocked">Очередь разблокирована</string> + <string name="queue_lock_warning">Заблокировав очередь Вы больше не сможете смахивать или перемещать выпуски.</string> + <string name="checkbox_do_not_show_again">Больше не показывать</string> <string name="clear_queue_label">Очистить очередь</string> <string name="undo">Отмена</string> <string name="move_to_top_label">Переместить вверх</string> @@ -299,6 +304,7 @@ URL файла: <!--Variable Speed--> <string name="download_plugin_label">Загрузить плагин</string> <string name="no_playback_plugin_title">Плагин не установлен</string> + <string name="no_playback_plugin_or_sonic_msg">Чтобы воспользоваться изменением скорости воспроизведения, рекомендуется включить встроенный медиапроигрыватель Sonic.</string> <string name="set_playback_speed_label">Скорость воспроизведения</string> <string name="enable_sonic">Включить Sonic</string> <!--Empty list labels--> @@ -321,6 +327,8 @@ URL файла: <string name="no_fav_episodes_label">Вы можете добавить выпуски в избранное долгим нажатием на них.</string> <string name="no_chapters_head_label">Нет оглавления</string> <string name="no_chapters_label">Этот выпуск не содержит оглавления.</string> + <string name="no_subscriptions_head_label">Подписки не оформлены</string> + <string name="no_subscriptions_label">чтобы подписаться на подкаст, нажмите на плюсик внизу.</string> <!--Preferences--> <string name="storage_pref">Хранилище</string> <string name="project_pref">Проект</string> @@ -370,6 +378,8 @@ URL файла: <string name="pref_pauseOnHeadsetDisconnect_title">Наушники отсоединены</string> <string name="pref_unpauseOnHeadsetReconnect_title">Наушники подключены обратно</string> <string name="pref_unpauseOnBluetoothReconnect_title">Bluetooth-соединение восстановлено</string> + <string name="pref_stream_over_download_title">Трансляция по сети в приоритете</string> + <string name="pref_stream_over_download_sum">Заменяет кнопки загрузки на кнопку трансляции в перечнях.</string> <string name="pref_mobileUpdate_title">Мобильные обновления</string> <string name="pref_mobileUpdate_sum">Выберите что допускается загружать при мобильном интернет-подключении</string> <string name="pref_mobileUpdate_refresh">Обновления каналов</string> @@ -378,23 +388,28 @@ URL файла: <string name="pref_mobileUpdate_episode_download">Загрузка выпусков</string> <string name="pref_mobileUpdate_streaming">Трансляции по сети</string> <string name="user_interface_label">Интерфейс</string> - <string name="pref_set_theme_title">Выбор темы</string> + <string name="pref_set_theme_title">Выбор оформления</string> <string name="pref_nav_drawer_items_title">Выбрать пункты боковой панели</string> <string name="pref_nav_drawer_items_sum">Изменение отображения пунктов меню боковой панели</string> <string name="pref_nav_drawer_feed_order_title">Упорядочить подписки</string> <string name="pref_nav_drawer_feed_order_sum">Выбрать порядок отображения подписок</string> <string name="pref_nav_drawer_feed_counter_title">Выбрать счётчик подписок</string> - <string name="pref_nav_drawer_feed_counter_sum">Выбрать какую информацию показывать в счётчике подписок. Влияет также на очерёдность подписок, если сортировка подписок производится по счётчику.</string> + <string name="pref_nav_drawer_feed_counter_sum">Какую информацию показывать в счётчике подписок. Влияет также на очерёдность подписок, если сортировка подписок производится по счётчику.</string> <string name="pref_set_theme_sum">Изменить тему оформления AntennaPod</string> <string name="pref_automatic_download_title">Автоматическая загрузка</string> <string name="pref_automatic_download_sum">Настроить автоматическую загрузку выпусков.</string> <string name="pref_autodl_wifi_filter_title">Включить фильтр Wi-Fi</string> <string name="pref_autodl_wifi_filter_sum">Разрешать автоматическую загрузку только для выбранных сетей Wi-Fi.</string> + <string name="autodl_wifi_filter_permission_title">Требуется разрешение</string> + <string name="autodl_wifi_filter_permission_message">Для работы фильтра Wi-Fi требуется разрешение на определение местоположения. Нажмите \"Разрешить\".</string> <string name="pref_automatic_download_on_battery_title">Загружать без зарядки</string> <string name="pref_automatic_download_on_battery_sum">Разрешать автоматическую загрузку когда батарея не заряжается</string> <string name="pref_parallel_downloads_title">Одновременные загрузки</string> <string name="pref_episode_cache_title">Кэш выпусков</string> <string name="pref_episode_cache_summary">Общее количество загруженных в кэш выпусков. Автоматическая загрузка будет приостановлена, если это количество будет достигнуто.</string> + <string name="pref_episode_cover_title">Использовать обложку выпуска</string> + <string name="pref_episode_cover_summary">Отображать обложку выпуска вместо обложки подкаста. если она отличается.</string> + <string name="pref_theme_title_use_system">Использовать системное оформление</string> <string name="pref_theme_title_light">Светлая</string> <string name="pref_theme_title_dark">Тёмная</string> <string name="pref_theme_title_trueblack">Чёрная (для AMOLED)</string> @@ -419,6 +434,7 @@ URL файла: <string name="pref_gpodnet_notifications_sum">Не затрагивает ошибки авторизации.</string> <string name="pref_playback_speed_title">Скорость воспроизведения</string> <string name="pref_playback_speed_sum">Настроить скорости воспроизведения</string> + <string name="pref_feed_playback_speed_sum">Скорость, с которой будут изначально воспроизводиться выпуски этого канала</string> <string name="pref_playback_time_respects_speed_title">Корректировка информации о медиа файлов с учетом скорости воспроизведения</string> <string name="pref_playback_time_respects_speed_sum">Отображаемые позиция и длительность адаптированы к скорости воспроизведения</string> <string name="pref_fast_forward">Интервал быстрой перемотки вперед</string> @@ -440,9 +456,19 @@ URL файла: <string name="pref_showDownloadReport_title">Показывать отчёт о загрузках</string> <string name="pref_showDownloadReport_sum">Если загрузка не удаётся, показывать отчёт с подробностями об ошибке.</string> <string name="pref_expand_notify_unsupport_toast">Версии Android ниже 4.1 не поддерживают расширенные уведомления.</string> + <string name="pref_enqueue_location_title">Размещение в очереди</string> + <string name="pref_enqueue_location_sum">Добавлять выпуски %1$s</string> + <string name="enqueue_location_back">в конец</string> + <string name="enqueue_location_front">в начало</string> + <string name="enqueue_location_after_current">за текущим выпуском</string> <string name="pref_smart_mark_as_played_disabled">Отключено</string> <string name="pref_image_cache_size_title">Размер кэша для изображений</string> <string name="pref_image_cache_size_sum">Размер дискового кэша для изображений</string> + <string name="view_mailing_list">Показать список рассылки</string> + <string name="bug_report_title">Сообщить об ошибке</string> + <string name="open_bug_tracker">Посетить систему отслеживания ошибок</string> + <string name="copy_to_clipboard">Скопировать в буфер</string> + <string name="copied_to_clipboard">Скопировано в буфер</string> <string name="experimental_pref">Экспериментальные настройки</string> <string name="pref_media_player_message">Выберите каким проигрывателем следует воспроизводить файлы</string> <string name="pref_current_value">Текущее значение: %1$s</string> @@ -476,8 +502,14 @@ URL файла: <string name="pref_delete_removes_from_queue_sum">Автоматически убирать выпуск из очереди при его удалении.</string> <!--About screen--> <string name="about_pref">О программе</string> + <string name="antennapod_version">Версия AntennaPod</string> <string name="developers">Разработчики</string> + <string name="developers_summary">Любой может помочь улучшить AntennaPod</string> <string name="translators">Переводчики</string> + <string name="translators_summary">Перевод выполнен пользователями AntennaPod при помощи Transifex</string> + <string name="privacy_policy">Политика конфиденциальности</string> + <string name="licenses">Лицензии</string> + <string name="licenses_summary">В AntennaPod используется другое отличное ПО</string> <!--Search--> <string name="search_hint">Найти выпуски</string> <string name="found_in_shownotes_label">Найдено в примечаниях к выпуску</string> @@ -599,6 +631,7 @@ URL файла: <string name="pref_restart_required">Следует перезапустить AntennaPod для применения настроек.</string> <!--Online feed view--> <string name="subscribe_label">Подписаться</string> + <string name="subscribing_label">Подписка оформляется…</string> <!--Content descriptions for image buttons--> <string name="rewind_label">Назад</string> <string name="fast_forward_label">Вперёд</string> @@ -721,5 +754,7 @@ URL файла: <string name="notification_channel_error_description">Показывается если что-то пошло не так, к примеру, неудавшаяся загрузка или синхронизация с gpodder.</string> <string name="import_bad_file">Недопустимый или поврежденный файл</string> <!--Widget settings--> + <string name="widget_settings">Настройки виджета</string> <string name="widget_create_button">Создать виджет</string> + <string name="widget_opacity">Непрозрачность</string> </resources> diff --git a/core/src/main/res/values-sv-rSE/strings.xml b/core/src/main/res/values-sv-rSE/strings.xml index 52f3f063d..2acb5256c 100644 --- a/core/src/main/res/values-sv-rSE/strings.xml +++ b/core/src/main/res/values-sv-rSE/strings.xml @@ -31,6 +31,8 @@ <string name="statistics_mode_normal">Beräkna faktisk speltid. Att spela två gånger räknas två gånger, men att markera som spelad räknas inte</string> <string name="statistics_mode_count_all">Summera alla podcasts som markerats som spelade</string> <string name="statistics_speed_not_counted">Notera: Uppspelningshastighet tas inte med i beräkningarna. </string> + <string name="statistics_reset_data">Återställ statistikdata</string> + <string name="statistics_reset_data_msg">Detta kommer att radera historiken av spelad tid för alla episoder. Är du säker på att du vill fortsätta?</string> <!--Main activity--> <string name="drawer_open">Öppna meny</string> <string name="drawer_close">Stäng meny</string> @@ -121,6 +123,7 @@ <string name="share_link_label">Dela episod-URL</string> <string name="share_link_with_position_label">Dela episod-URL med position</string> <string name="share_file_label">Dela fil</string> + <string name="share_website_url_label">Dela Webbsidans URL</string> <string name="share_feed_url_label">Dela flödets URL</string> <string name="share_item_url_label">Dela mediafilens URL</string> <string name="share_item_url_with_position_label">Dela mediafilens URL med position</string> @@ -258,6 +261,8 @@ <string name="unlock_queue">Lås upp Kön</string> <string name="queue_locked">Kön låst</string> <string name="queue_unlocked">Kön upplåst</string> + <string name="queue_lock_warning">Om du låser kön kan du inte längre svepa eller ordna om episoder.</string> + <string name="checkbox_do_not_show_again">Visa inte igen</string> <string name="clear_queue_label">Rensa kön</string> <string name="undo">Ångra</string> <string name="move_to_top_label">Flytta längst upp</string> @@ -278,6 +283,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Ladda ner tillägg</string> <string name="no_playback_plugin_title">Tillägg ej installerat</string> + <string name="no_playback_plugin_or_sonic_msg">Vi rekommenderar att den inbyggda Sonic-spelaren används för att kunna variera uppspelningshastigheten.</string> <string name="set_playback_speed_label">Uppspelningshastigheter</string> <string name="enable_sonic">Aktivera Sonic</string> <!--Empty list labels--> @@ -300,6 +306,8 @@ <string name="no_fav_episodes_label">Du kan lägga till episoder i favoriter genom att lång-trycka på dem.</string> <string name="no_chapters_head_label">Inga kapitel</string> <string name="no_chapters_label">Denna episod har inga kapitel.</string> + <string name="no_subscriptions_head_label">Inga prenumerationer</string> + <string name="no_subscriptions_label">Tryck på plus-ikonen nedan för att prenumerera på podcasts.</string> <!--Preferences--> <string name="storage_pref">Lagring</string> <string name="project_pref">Projekt</string> @@ -349,6 +357,8 @@ <string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar bortkopplas</string> <string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar återanslutna</string> <string name="pref_unpauseOnBluetoothReconnect_title">Blutetooth återansluts</string> + <string name="pref_stream_over_download_title">Föredra Strömmning</string> + <string name="pref_stream_over_download_sum">Visa strömningsknappen istället för nedlanningsknappen i listor.</string> <string name="pref_mobileUpdate_title">Mobila uppdateringar</string> <string name="pref_mobileUpdate_sum">Välj vad som ska tillåtas över mobila dataanslutningar</string> <string name="pref_mobileUpdate_refresh">Flödesuppdatering</string> @@ -369,11 +379,16 @@ <string name="pref_automatic_download_sum">Konfigurera automatisk nedladdning av episoder.</string> <string name="pref_autodl_wifi_filter_title">Aktivera WiFi filtrering</string> <string name="pref_autodl_wifi_filter_sum">Tillåt automatisk nedladdning endast för utvalda WiFi-nätverk.</string> + <string name="autodl_wifi_filter_permission_title">Rättighet krävs</string> + <string name="autodl_wifi_filter_permission_message">Platsrättigheten krävs för Wi-Fi filtrering. Tryck för att tilldela rättigheten.</string> <string name="pref_automatic_download_on_battery_title">Nedladdning vid batteridrift</string> <string name="pref_automatic_download_on_battery_sum">Tillåt automatisk nedladdning när batteriet inte laddas</string> <string name="pref_parallel_downloads_title">Parallella nedladdningar</string> <string name="pref_episode_cache_title">Episodcache</string> <string name="pref_episode_cache_summary">Totalt antal nedladdade epidoder som ligger i enhetens cache. Automatisk nedladdning kommer att vänta om detta antal nås.</string> + <string name="pref_episode_cover_title">Använd Episodomslag</string> + <string name="pref_episode_cover_summary">Använd episodens egna omslag om tillgängligt. Appen kommer alltid att använda podcastens omslagsbild om rutan lämnas tom.</string> + <string name="pref_theme_title_use_system">Använd systemtemat</string> <string name="pref_theme_title_light">Ljust</string> <string name="pref_theme_title_dark">Mörkt</string> <string name="pref_theme_title_trueblack">Svart (AMOLED redo)</string> @@ -398,6 +413,7 @@ <string name="pref_gpodnet_notifications_sum">Denna inställning påverkar inte autentiseringsfel.</string> <string name="pref_playback_speed_title">Uppspelningshastigheter</string> <string name="pref_playback_speed_sum">Anpassa de tillgängliga hastigheterna för variabel uppspelningshastighet.</string> + <string name="pref_feed_playback_speed_sum">Hastigheten att använda när ljuduppspelning startas för episoder i detta flöde</string> <string name="pref_playback_time_respects_speed_title">Justera mediainfo till uppspelningshastigheten</string> <string name="pref_playback_time_respects_speed_sum">Visad position och totallängd anpassas till uppspelningshastigheten</string> <string name="pref_fast_forward">Snabbspolningslängd</string> @@ -419,14 +435,25 @@ <string name="pref_showDownloadReport_title">Visa nedladdningsrapport</string> <string name="pref_showDownloadReport_sum">Visa en rapport med detaljer om felet när nedladdningar misslyckas.</string> <string name="pref_expand_notify_unsupport_toast">Androidversioner före 4.1 har inte stöd för expanderade aviseringar.</string> + <string name="pref_enqueue_location_title">Köplats</string> + <string name="pref_enqueue_location_sum">Lägg till episoder i: %1$s</string> + <string name="enqueue_location_back">Slutet</string> + <string name="enqueue_location_front">Början</string> + <string name="enqueue_location_after_current">Efter nuvarande episod</string> <string name="pref_smart_mark_as_played_disabled">Avaktiverad</string> <string name="pref_image_cache_size_title">Bildcachestorlek</string> <string name="pref_image_cache_size_sum">Storleken på bildcachen på disken.</string> + <string name="view_mailing_list">Visa mailinglistan</string> + <string name="bug_report_title">Rapportera bugg</string> + <string name="open_bug_tracker">Öppna buggtrackern</string> + <string name="copy_to_clipboard">Kopiera till urklipp</string> + <string name="copied_to_clipboard">Kopierat till urklipp</string> <string name="experimental_pref">Experimentellt</string> <string name="pref_media_player_message">Välj vilken mediaspelare som ska spela filer</string> <string name="pref_current_value">Nuvarande värde: %1$s</string> <string name="pref_proxy_title">Proxy</string> <string name="pref_proxy_sum">Använd en nätverksproxy</string> + <string name="pref_faq">Frekvent Frågade Frågor</string> <string name="pref_no_browser_found">Ingen webbläsare hittades.</string> <string name="pref_cast_title">Chromecast-stöd</string> <string name="pref_cast_message_play_flavor">Aktivera stöd för fjärruppspelning av media på Cast-enheter (såsom Chromecast, Ljudanläggningar eller Android TV)</string> @@ -454,6 +481,14 @@ <string name="pref_delete_removes_from_queue_sum">Ta automatiskt bort episoder från kön när de raderas.</string> <!--About screen--> <string name="about_pref">Om</string> + <string name="antennapod_version">AntennaPod version</string> + <string name="developers">Utvecklare</string> + <string name="developers_summary">Alla kan hjälpa till att göra AntennaPod bättre</string> + <string name="translators">Översättare</string> + <string name="translators_summary">Översättningar skapas av AntennaPods användare med hjälp av Transifex</string> + <string name="privacy_policy">Integritetspolicy</string> + <string name="licenses">Licenser</string> + <string name="licenses_summary">AntennaPod förlitar sig på annan bra programvara</string> <!--Search--> <string name="search_hint">Sök efter episoder</string> <string name="found_in_shownotes_label">Hittad i shownotes</string> @@ -567,6 +602,7 @@ <string name="pref_restart_required">AntennaPod behöver startas om för att denna inställning ska gälla.</string> <!--Online feed view--> <string name="subscribe_label">Prenumerera</string> + <string name="subscribing_label">Prenumererar...</string> <!--Content descriptions for image buttons--> <string name="rewind_label">Backa</string> <string name="fast_forward_label">Snabbspola</string> @@ -689,4 +725,7 @@ <string name="notification_channel_error_description">Visas om något blev fel, exempelvis om nedladdning eller gpodder synkronisering misslyckas.</string> <string name="import_bad_file">Ogiltig/korrupt fil</string> <!--Widget settings--> + <string name="widget_settings">Widgetinställningar</string> + <string name="widget_create_button">Skapa widget</string> + <string name="widget_opacity">Opacitet</string> </resources> diff --git a/core/src/main/res/values-v16/colors.xml b/core/src/main/res/values-v16/colors.xml deleted file mode 100644 index 4154280e8..000000000 --- a/core/src/main/res/values-v16/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <color name="selection_background_color_dark">#484B4D</color> -</resources>
\ No newline at end of file diff --git a/core/src/main/res/values-v16/styles.xml b/core/src/main/res/values-v16/styles.xml deleted file mode 100644 index 947e43f38..000000000 --- a/core/src/main/res/values-v16/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <style name="AntennaPod.TextView.Heading" parent="@android:style/TextAppearance.Medium"> - <item name="android:textSize">@dimen/text_size_large</item> - <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:fontFamily">sans-serif-light</item> - </style> -</resources>
\ No newline at end of file diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml index c6668675f..53cf7b211 100644 --- a/core/src/main/res/values/attrs.xml +++ b/core/src/main/res/values/attrs.xml @@ -28,7 +28,6 @@ <attr name="ic_folder" format="reference"/> <attr name="type_audio" format="reference"/> <attr name="type_video" format="reference"/> - <attr name="borderless_button" format="reference"/> <attr name="overlay_drawable" format="reference"/> <attr name="dragview_background" format="reference"/> <attr name="dragview_float_background" format="reference"/> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 7ba02db3d..88fa07049 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -7,7 +7,7 @@ <string name="app_name" translate="false">AntennaPod</string> <string name="provider_authority" translate="false">de.danoeh.antennapod.provider</string> <string name="feed_update_receiver_name">Update Subscriptions</string> - <string name="feeds_label">Feeds</string> + <string name="feeds_label">Podcasts</string> <string name="statistics_label">Statistics</string> <string name="add_feed_label">Add Podcast</string> <string name="episodes_label">Episodes</string> @@ -29,6 +29,8 @@ <string name="gpodnet_auth_label">gpodder.net Login</string> <string name="episode_cache_full_title">Episode cache full</string> <string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string> + <string name="playback_statistics_label">Playback</string> + <string name="download_statistics_label">Downloads</string> <!-- Statistics fragment --> <string name="total_time_listened_to_podcasts">Total time of podcasts played:</string> @@ -40,6 +42,9 @@ <string name="statistics_reset_data">Reset statistics data</string> <string name="statistics_reset_data_msg">This will erase the history of duration played for all episodes. Are you sure you want to proceed?</string> + <!-- Download Statistics fragment --> + <string name="total_size_downloaded_podcasts">Total size of downloaded podcasts:</string> + <!-- Main activity --> <string name="drawer_open">Open menu</string> <string name="drawer_close">Close menu</string> @@ -113,9 +118,10 @@ <item quantity="other">%d days after finishing</item> </plurals> <string name="num_selected_label">%d selected</string> + <string name="loading_more">Loading more…</string> <!-- 'Add Feed' Activity labels --> - <string name="feedurl_label">Feed URL</string> + <string name="feedurl_label">Podcast feed URL</string> <string name="etxtFeedurlHint">www.example.com/feed</string> <string name="txtvfeedurl_label">Add Podcast by URL</string> <string name="browse_gpoddernet_label">Browse gpodder.net</string> @@ -141,7 +147,7 @@ <string name="share_link_with_position_label">Share Episode URL with Position</string> <string name="share_file_label">Share File</string> <string name="share_website_url_label">Share Website URL</string> - <string name="share_feed_url_label">Share Feed URL</string> + <string name="share_feed_url_label">Share Podcast URL</string> <string name="share_item_url_label">Share Media File URL</string> <string name="share_item_url_with_position_label">Share Media File URL with Position</string> <string name="feed_delete_confirmation_msg">Please confirm that you want to delete the podcast \"%1$s\" and ALL its episodes (including downloaded episodes).</string> @@ -184,11 +190,14 @@ <string name="removed_new_flag_label">Removed \"new\" flag</string> <string name="mark_read_label">Mark as played</string> <string name="marked_as_read_label">Marked as played</string> + <string name="mark_read_no_media_label">Mark as read</string> + <string name="marked_as_read_no_media_label">Marked as read</string> <plurals name="marked_read_batch_label"> <item quantity="one">%d episode marked as played.</item> <item quantity="other">%d episodes marked as played.</item> </plurals> <string name="mark_unread_label">Mark as unplayed</string> + <string name="mark_unread_label_no_media">Mark as unread</string> <plurals name="marked_unread_batch_label"> <item quantity="one">%d episode marked as unplayed.</item> <item quantity="other">%d episodes marked as unplayed.</item> @@ -336,9 +345,11 @@ <!-- Preferences --> <string name="storage_pref">Storage</string> + <string name="storage_sum">Episode auto delete, Export, Import</string> <string name="project_pref">Project</string> <string name="queue_label">Queue</string> <string name="integrations_label">Integrations</string> + <string name="integrations_sum">External services</string> <string name="automation">Automation</string> <string name="download_pref_details">Details</string> <string name="import_export_pref">Import/Export</string> @@ -370,9 +381,11 @@ <string name="pref_favorite_keeps_episodes_sum">Keep episodes when they are marked Favorite</string> <string name="pref_favorite_keeps_episodes_title">Keep Favorite Episodes</string> <string name="playback_pref">Playback</string> + <string name="playback_pref_sum">Headphone controls, Skip intervals, Queue</string> <string name="network_pref">Network</string> + <string name="network_pref_sum">Update interval, Download controls, Mobile data</string> <string name="pref_autoUpdateIntervallOrTime_title">Update Interval or Time of Day</string> - <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the feeds automatically</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the podcasts automatically</string> <string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small></string> <string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string> <string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string> @@ -387,12 +400,13 @@ <string name="pref_stream_over_download_sum">Display stream button instead of download button in lists.</string> <string name="pref_mobileUpdate_title">Mobile Updates</string> <string name="pref_mobileUpdate_sum">Select what should be allowed over the mobile data connection</string> - <string name="pref_mobileUpdate_refresh">Feed refresh</string> + <string name="pref_mobileUpdate_refresh">Podcast refresh</string> <string name="pref_mobileUpdate_images">Cover images</string> <string name="pref_mobileUpdate_auto_download">Auto download</string> <string name="pref_mobileUpdate_episode_download">Episode download</string> <string name="pref_mobileUpdate_streaming">Streaming</string> <string name="user_interface_label">User Interface</string> + <string name="user_interface_sum">Appearance, Subscription order, Lockscreen</string> <string name="pref_set_theme_title">Select Theme</string> <string name="pref_nav_drawer_items_title">Set Navigation Drawer items</string> <string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer.</string> @@ -439,7 +453,7 @@ <string name="pref_gpodnet_notifications_sum">This setting does not apply to authentication errors.</string> <string name="pref_playback_speed_title">Playback Speeds</string> <string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string> - <string name="pref_feed_playback_speed_sum">The speed to use when starting audio playback for episodes in this feed</string> + <string name="pref_feed_playback_speed_sum">The speed to use when starting audio playback for episodes in this podcast</string> <string name="pref_playback_time_respects_speed_title">Adjust media info to playback speed</string> <string name="pref_playback_time_respects_speed_sum">Displayed position and duration are adapted to playback speed</string> <string name="pref_fast_forward">Fast Forward Skip Time</string> @@ -662,7 +676,7 @@ <string name="episode_filters_exclude">Exclude</string> <string name="episode_filters_hint">Single words \n\"Multiple Words\"</string> <string name="keep_updated">Keep Updated</string> - <string name="keep_updated_summary">Include this feed when (auto-)refreshing all feeds</string> + <string name="keep_updated_summary">Include this podcast when (auto-)refreshing all podcasts</string> <string name="auto_download_disabled_globally">Auto download is disabled in the main AntennaPod settings</string> <!-- Progress information --> diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index 6a82adf74..f5b43629c 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -281,6 +281,7 @@ <style name="AntennaPod.TextView.Heading" parent="@android:style/TextAppearance.Medium"> <item name="android:textSize">@dimen/text_size_large</item> <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:fontFamily">sans-serif-light</item> </style> <style name="AntennaPod.TextView.ListItemPrimaryTitle" parent="@android:style/TextAppearance.Small"> diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java index 55d804203..c92ce3e74 100644 --- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java @@ -3,6 +3,10 @@ package de.danoeh.antennapod.core; import android.content.Context; import android.util.Log; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.android.gms.common.GooglePlayServicesNotAvailableException; +import com.google.android.gms.common.GooglePlayServicesRepairableException; +import com.google.android.gms.security.ProviderInstaller; import de.danoeh.antennapod.core.cast.CastManager; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; @@ -57,10 +61,21 @@ public class ClientConfig { } else { Log.v(TAG, "Cast is disabled. All Cast-related initialization will be skipped."); } + installSslProvider(context); SleepTimerPreferences.init(context); RxJavaErrorHandlerSetup.setupRxJavaErrorHandler(); NotificationUtils.createChannels(context); initialized = true; } + private static void installSslProvider(Context context) { + try { + ProviderInstaller.installIfNeeded(context); + } catch (GooglePlayServicesRepairableException e) { + e.printStackTrace(); + GoogleApiAvailability.getInstance().showErrorNotification(context, e.getConnectionStatusCode()); + } catch (GooglePlayServicesNotAvailableException e) { + e.printStackTrace(); + } + } } |