diff options
author | Tom Hennen <tom.hennen@gmail.com> | 2015-08-15 14:39:11 -0400 |
---|---|---|
committer | Tom Hennen <tom.hennen@gmail.com> | 2015-08-15 14:39:11 -0400 |
commit | 0dcc1a33f171471d09dd64093df385a173a95271 (patch) | |
tree | 2b63d30574e093c717a8b9021441724339c77cab /core/src | |
parent | bf2ba3b7c72f00e70d64fbd938a8f028cb75f3cd (diff) | |
parent | 928c438268ec4eab6f05bf2aa60f24e2d10156c1 (diff) | |
download | AntennaPod-0dcc1a33f171471d09dd64093df385a173a95271.zip |
merged version 1.3 to master
Diffstat (limited to 'core/src')
80 files changed, 2986 insertions, 2130 deletions
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java index 2a2d6414a..2727b1447 100644 --- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java +++ b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java @@ -13,6 +13,7 @@ public class DateUtilsTest extends AndroidTestCase { public void testParseDateWithMicroseconds() throws Exception { GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); Date expected = new Date(exp.getTimeInMillis() + 963); Date actual = DateUtils.parse("2015-03-28T13:31:04.963870"); assertEquals(expected, actual); @@ -20,6 +21,7 @@ public class DateUtilsTest extends AndroidTestCase { public void testParseDateWithCentiseconds() throws Exception { GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); Date expected = new Date(exp.getTimeInMillis() + 960); Date actual = DateUtils.parse("2015-03-28T13:31:04.96"); assertEquals(expected, actual); @@ -27,6 +29,7 @@ public class DateUtilsTest extends AndroidTestCase { public void testParseDateWithDeciseconds() throws Exception { GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); Date expected = new Date(exp.getTimeInMillis() + 900); Date actual = DateUtils.parse("2015-03-28T13:31:04.9"); assertEquals(expected.getTime()/1000, actual.getTime()/1000); @@ -66,6 +69,14 @@ public class DateUtilsTest extends AndroidTestCase { assertEquals(expected, actual); } + public void testParseDateWithTimezoneName2() throws Exception { + GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 6, 31, 0); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); + Date expected = new Date(exp.getTimeInMillis()); + Date actual = DateUtils.parse("Sat, 28 Mar 2015 01:31 EST"); + assertEquals(expected, actual); + } + public void testParseDateWithTimeZoneOffset() throws Exception { GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 12, 16, 12); exp.setTimeZone(TimeZone.getTimeZone("UTC")); @@ -74,4 +85,20 @@ public class DateUtilsTest extends AndroidTestCase { assertEquals(expected, actual); } + public void testAsctime() throws Exception { + GregorianCalendar exp = new GregorianCalendar(2011, 4, 25, 12, 33, 00); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); + Date expected = new Date(exp.getTimeInMillis()); + Date actual = DateUtils.parse("Wed, 25 May 2011 12:33:00"); + assertEquals(expected, actual); + } + + public void testMultipleConsecutiveSpaces() throws Exception { + GregorianCalendar exp = new GregorianCalendar(2010, 2, 23, 6, 6, 26); + exp.setTimeZone(TimeZone.getTimeZone("UTC")); + Date expected = new Date(exp.getTimeInMillis()); + Date actual = DateUtils.parse("Tue, 23 Mar 2010 01:06:26 -0500"); + assertEquals(expected, actual); + } + } diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index 3ec519844..17dcb4ad8 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -23,6 +23,9 @@ <service android:name=".service.GpodnetSyncService" android:enabled="true" /> + <service + android:name=".service.FeedMediaSizeService" + android:enabled="true" /> <receiver android:name=".receiver.MediaButtonReceiver" diff --git a/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java index 3bc1ce4eb..1064e98ac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java @@ -20,5 +20,4 @@ public interface ApplicationCallbacks { */ public Intent getStorageErrorActivity(Context context); - public void setUpdateInterval(long updateInterval); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java index 1a2671555..6619e706b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java @@ -21,7 +21,5 @@ public class ClientConfig { public static FlattrCallbacks flattrCallbacks; - public static StorageCallbacks storageCallbacks; - public static DBTasksCallbacks dbTasksCallbacks; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java deleted file mode 100644 index 5d1a0fffc..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.danoeh.antennapod.core; - -import android.database.sqlite.SQLiteDatabase; - -/** - * Callbacks for the classes in the storage package of the core module. - */ -public interface StorageCallbacks { - - /** - * Returns the current version of the database. - * - * @return The non-negative version number of the database. - */ - public int getDatabaseVersion(); - - /** - * Upgrades the given database from an old version to a newer version. - * - * @param db The database that is supposed to be upgraded. - * @param oldVersion The old version of the database. - * @param newVersion The version that the database is supposed to be upgraded to. - */ - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); - - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java index a13130082..732fa2d27 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java @@ -12,6 +12,7 @@ import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -85,15 +86,18 @@ public class DownloadObserver { if (downloadService == null) { connectToDownloadService(); } - callback.onContentChanged(); + if (downloadService != null) { + callback.onContentChanged(downloadService.getDownloads()); + } else { + // the service is gone, there are no more downloads. + callback.onContentChanged(new ArrayList<Downloader>()); + } startRefresher(); } }; public interface Callback { - void onContentChanged(); - - void onDownloadDataAvailable(List<Downloader> downloaderList); + void onContentChanged(List<Downloader> downloaderList); } private void connectToDownloadService() { @@ -116,7 +120,7 @@ public class DownloadObserver { Log.d(TAG, "Connection to service established"); List<Downloader> downloaderList = downloadService.getDownloads(); if (downloaderList != null && !downloaderList.isEmpty()) { - callback.onDownloadDataAvailable(downloaderList); + callback.onContentChanged(downloaderList); startRefresher(); } } @@ -156,12 +160,14 @@ public class DownloadObserver { handler.post(new Runnable() { @Override public void run() { - callback.onContentChanged(); if (downloadService != null) { List<Downloader> downloaderList = downloadService.getDownloads(); + callback.onContentChanged(downloaderList); if (downloaderList == null || downloaderList.isEmpty()) { Thread.currentThread().interrupt(); } + } else { + callback.onContentChanged(new ArrayList<Downloader>()); } } }); diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java index 5d2d5d441..f91d4557e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java @@ -90,7 +90,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke return ExitCode.NO_TOKEN; } - if (!NetworkUtils.networkAvailable(context)) { + if (!NetworkUtils.networkAvailable()) { return ExitCode.NO_NETWORK; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java index c0d8049db..edd69f15b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java +++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java @@ -6,7 +6,7 @@ import android.net.Uri; * Classes that implement this interface provide access to an image resource that can * be loaded by the Picasso library. */ -public interface PicassoImageResource { +public interface ImageResource { /** * This scheme should be used by PicassoImageResources to diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java deleted file mode 100644 index 09fe0d654..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java +++ /dev/null @@ -1,527 +0,0 @@ -package de.danoeh.antennapod.core.asynctask; - -import android.content.ContentResolver; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.MediaMetadataRetriever; -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Response; -import com.squareup.picasso.Cache; -import com.squareup.picasso.LruCache; -import com.squareup.picasso.OkHttpDownloader; -import com.squareup.picasso.Picasso; -import com.squareup.picasso.Request; -import com.squareup.picasso.RequestHandler; -import com.squareup.picasso.Transformation; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import de.danoeh.antennapod.core.service.download.HttpDownloader; -import de.danoeh.antennapod.core.storage.DBReader; - -/** - * Provides access to Picasso instances. - */ -public class PicassoProvider { - - private static final String TAG = "PicassoProvider"; - - private static final boolean DEBUG = false; - - private static ExecutorService executorService; - private static Cache memoryCache; - - private static synchronized ExecutorService getExecutorService() { - if (executorService == null) { - executorService = Executors.newFixedThreadPool(3); - } - return executorService; - } - - private static synchronized Cache getMemoryCache(Context context) { - if (memoryCache == null) { - memoryCache = new LruCache(context); - } - return memoryCache; - } - - private static volatile boolean picassoSetup = false; - - public static synchronized void setupPicassoInstance(Context appContext) { - if (picassoSetup) { - return; - } - OkHttpClient client = new OkHttpClient(); - client.interceptors().add(new BasicAuthenticationInterceptor(appContext)); - Picasso picasso = new Picasso.Builder(appContext) - .indicatorsEnabled(DEBUG) - .loggingEnabled(DEBUG) - .downloader(new OkHttpDownloader(client)) - .addRequestHandler(new MediaRequestHandler(appContext)) - .executor(getExecutorService()) - .memoryCache(getMemoryCache(appContext)) - .listener(new Picasso.Listener() { - @Override - public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) { - Log.e(TAG, "Failed to load Uri:" + uri.toString()); - e.printStackTrace(); - } - }) - .build(); - Picasso.setSingletonInstance(picasso); - picassoSetup = true; - } - - private static class BasicAuthenticationInterceptor implements Interceptor { - - private final Context context; - - public BasicAuthenticationInterceptor(Context context) { - this.context = context; - } - - @Override - public Response intercept(Chain chain) throws IOException { - com.squareup.okhttp.Request request = chain.request(); - String url = request.urlString(); - String authentication = DBReader.getImageAuthentication(context, url); - - if(TextUtils.isEmpty(authentication)) { - Log.d(TAG, "no credentials for '" + url + "'"); - return chain.proceed(request); - } - - // add authentication - String[] auth = authentication.split(":"); - String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1"); - com.squareup.okhttp.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; - } - } - } - - private static class MediaRequestHandler extends RequestHandler { - - final Context context; - - public MediaRequestHandler(Context context) { - super(); - this.context = context; - } - - @Override - public boolean canHandleRequest(Request data) { - return StringUtils.equals(data.uri.getScheme(), PicassoImageResource.SCHEME_MEDIA); - } - - @Override - public Result load(Request data, int networkPolicy) throws IOException { - Bitmap bitmap = null; - MediaMetadataRetriever mmr = null; - try { - mmr = new MediaMetadataRetriever(); - mmr.setDataSource(data.uri.getPath()); - byte[] image = mmr.getEmbeddedPicture(); - if (image != null) { - bitmap = decodeStreamFromByteArray(data, image); - } - } catch (RuntimeException e) { - Log.e(TAG, "Failed to decode image in media file", e); - } finally { - if (mmr != null) { - mmr.release(); - } - } - - if (bitmap == null) { - // this should never, happen, but sometimes it does, so fallback - // check for fallback Uri - String fallbackParam = data.uri.getQueryParameter(PicassoImageResource.PARAM_FALLBACK); - if (fallbackParam != null) { - Uri fallback = Uri.parse(fallbackParam); - bitmap = decodeStreamFromFile(data, fallback); - } - } - return new Result(bitmap, Picasso.LoadedFrom.DISK); - - } - - /* Copied/Adapted from Picasso RequestHandler classes */ - - private Bitmap decodeStreamFromByteArray(Request data, byte[] bytes) throws IOException { - - final BitmapFactory.Options options = createBitmapOptions(data); - final ByteArrayInputStream in = new ByteArrayInputStream(bytes); - in.mark(0); - if (requiresInSampleSize(options)) { - try { - BitmapFactory.decodeStream(in, null, options); - } finally { - in.reset(); - } - calculateInSampleSize(data.targetWidth, data.targetHeight, options, data); - } - try { - return BitmapFactory.decodeStream(in, null, options); - } finally { - IOUtils.closeQuietly(in); - } - } - - private Bitmap decodeStreamFromFile(Request data, Uri uri) throws IOException { - ContentResolver contentResolver = context.getContentResolver(); - final BitmapFactory.Options options = createBitmapOptions(data); - if (requiresInSampleSize(options)) { - InputStream is = null; - try { - is = contentResolver.openInputStream(uri); - BitmapFactory.decodeStream(is, null, options); - } finally { - IOUtils.closeQuietly(is); - } - calculateInSampleSize(data.targetWidth, data.targetHeight, options, data); - } - InputStream is = contentResolver.openInputStream(uri); - try { - return BitmapFactory.decodeStream(is, null, options); - } finally { - IOUtils.closeQuietly(is); - } - } - - private BitmapFactory.Options createBitmapOptions(Request data) { - final boolean justBounds = data.hasSize(); - final boolean hasConfig = data.config != null; - BitmapFactory.Options options = null; - if (justBounds || hasConfig) { - options = new BitmapFactory.Options(); - options.inJustDecodeBounds = justBounds; - if (hasConfig) { - options.inPreferredConfig = data.config; - } - } - return options; - } - - private static boolean requiresInSampleSize(BitmapFactory.Options options) { - return options != null && options.inJustDecodeBounds; - } - - private static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options, - Request request) { - calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options, - request); - } - - private static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height, - BitmapFactory.Options options, Request request) { - int sampleSize = 1; - if (height > reqHeight || width > reqWidth) { - final int heightRatio; - final int widthRatio; - if (reqHeight == 0) { - sampleSize = (int) Math.floor((float) width / (float) reqWidth); - } else if (reqWidth == 0) { - sampleSize = (int) Math.floor((float) height / (float) reqHeight); - } else { - heightRatio = (int) Math.floor((float) height / (float) reqHeight); - widthRatio = (int) Math.floor((float) width / (float) reqWidth); - sampleSize = request.centerInside - ? Math.max(heightRatio, widthRatio) - : Math.min(heightRatio, widthRatio); - } - } - options.inSampleSize = sampleSize; - options.inJustDecodeBounds = false; - } - } - - public static final int BLUR_RADIUS = 1; - public static final int BLUR_IMAGE_SIZE = 100; - public static final String BLUR_KEY = "blur"; - - public static final Transformation blurTransformation = new Transformation() { - @Override - public Bitmap transform(Bitmap source) { - Bitmap result = fastblur(source, BLUR_RADIUS); - if (result == null) { - // just return the original - // for some reason we couldn't transform it. - return source; - } - source.recycle(); - return result; - } - - @Override - public String key() { - return BLUR_KEY; - } - }; - - public static Bitmap fastblur(Bitmap sentBitmap, int radius) { - - // Stack Blur v1.0 from - // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html - // - // Java Author: Mario Klingemann <mario at quasimondo.com> - // http://incubator.quasimondo.com - // created Feburary 29, 2004 - // Android port : Yahel Bouaziz <yahel at kayenko.com> - // http://www.kayenko.com - // ported april 5th, 2012 - - // This is a compromise between Gaussian Blur and Box blur - // It creates much better looking blurs than Box Blur, but is - // 7x faster than my Gaussian Blur implementation. - // - // I called it Stack Blur because this describes best how this - // filter works internally: it creates a kind of moving stack - // of colors whilst scanning through the image. Thereby it - // just has to add one new block of color to the right side - // of the stack and remove the leftmost color. The remaining - // colors on the topmost layer of the stack are either added on - // or reduced by one, depending on if they are on the right or - // on the left side of the stack. - // - // If you are using this algorithm in your code please add - // the following line: - // - // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> - Bitmap.Config config = sentBitmap.getConfig(); - if (config == null) { - // Sometimes the config can be null, in those cases - // we don't do a transform. - return null; - } - - Bitmap bitmap = sentBitmap.copy(config, true); - - if (radius < 1) { - return (null); - } - - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - - int[] pix = new int[w * h]; - Log.e("pix", w + " " + h + " " + pix.length); - bitmap.getPixels(pix, 0, w, 0, 0, w, h); - - int wm = w - 1; - int hm = h - 1; - int wh = w * h; - int div = radius + radius + 1; - - int r[] = new int[wh]; - int g[] = new int[wh]; - int b[] = new int[wh]; - int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; - int vmin[] = new int[Math.max(w, h)]; - - int divsum = (div + 1) >> 1; - divsum *= divsum; - int dv[] = new int[256 * divsum]; - for (i = 0; i < 256 * divsum; i++) { - dv[i] = (i / divsum); - } - - yw = yi = 0; - - int[][] stack = new int[div][3]; - int stackpointer; - int stackstart; - int[] sir; - int rbs; - int r1 = radius + 1; - int routsum, goutsum, boutsum; - int rinsum, ginsum, binsum; - - for (y = 0; y < h; y++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - for (i = -radius; i <= radius; i++) { - p = pix[yi + Math.min(wm, Math.max(i, 0))]; - sir = stack[i + radius]; - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - rbs = r1 - Math.abs(i); - rsum += sir[0] * rbs; - gsum += sir[1] * rbs; - bsum += sir[2] * rbs; - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - } - stackpointer = radius; - - for (x = 0; x < w; x++) { - - r[yi] = dv[rsum]; - g[yi] = dv[gsum]; - b[yi] = dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (y == 0) { - vmin[x] = Math.min(x + radius + 1, wm); - } - p = pix[yw + vmin[x]]; - - sir[0] = (p & 0xff0000) >> 16; - sir[1] = (p & 0x00ff00) >> 8; - sir[2] = (p & 0x0000ff); - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[(stackpointer) % div]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi++; - } - yw += w; - } - for (x = 0; x < w; x++) { - rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; - yp = -radius * w; - for (i = -radius; i <= radius; i++) { - yi = Math.max(0, yp) + x; - - sir = stack[i + radius]; - - sir[0] = r[yi]; - sir[1] = g[yi]; - sir[2] = b[yi]; - - rbs = r1 - Math.abs(i); - - rsum += r[yi] * rbs; - gsum += g[yi] * rbs; - bsum += b[yi] * rbs; - - if (i > 0) { - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - } else { - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - } - - if (i < hm) { - yp += w; - } - } - yi = x; - stackpointer = radius; - for (y = 0; y < h; y++) { - // Preserve alpha channel: ( 0xff000000 & pix[yi] ) - pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; - - rsum -= routsum; - gsum -= goutsum; - bsum -= boutsum; - - stackstart = stackpointer - radius + div; - sir = stack[stackstart % div]; - - routsum -= sir[0]; - goutsum -= sir[1]; - boutsum -= sir[2]; - - if (x == 0) { - vmin[y] = Math.min(y + r1, hm) * w; - } - p = x + vmin[y]; - - sir[0] = r[p]; - sir[1] = g[p]; - sir[2] = b[p]; - - rinsum += sir[0]; - ginsum += sir[1]; - binsum += sir[2]; - - rsum += rinsum; - gsum += ginsum; - bsum += binsum; - - stackpointer = (stackpointer + 1) % div; - sir = stack[stackpointer]; - - routsum += sir[0]; - goutsum += sir[1]; - boutsum += sir[2]; - - rinsum -= sir[0]; - ginsum -= sir[1]; - binsum -= sir[2]; - - yi += w; - } - } - - Log.e("pix", w + " " + h + " " + pix.length); - bitmap.setPixels(pix, 0, w, 0, 0, w, h); - - return (bitmap); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java index ba1add895..fea2bbb2b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java +++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java @@ -4,7 +4,7 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.util.Log; -import de.danoeh.antennapod.core.BuildConfig; + import de.danoeh.antennapod.core.R; /** @@ -12,12 +12,16 @@ import de.danoeh.antennapod.core.R; * classes can handle events like confirmation or cancellation. */ public abstract class ConfirmationDialog { - private static final String TAG = "ConfirmationDialog"; - Context context; + private static final String TAG = ConfirmationDialog.class.getSimpleName(); + + protected Context context; int titleId; int messageId; + int positiveText; + int negativeText; + public ConfirmationDialog(Context context, int titleId, int messageId) { this.context = context; this.titleId = titleId; @@ -25,18 +29,26 @@ public abstract class ConfirmationDialog { } public void onCancelButtonPressed(DialogInterface dialog) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Dialog was cancelled"); + Log.d(TAG, "Dialog was cancelled"); dialog.dismiss(); } + public void setPositiveText(int id) { + this.positiveText = id; + } + + public void setNegativeText(int id) { + this.negativeText = id; + } + + public abstract void onConfirmButtonPressed(DialogInterface dialog); public final AlertDialog createNewDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(titleId); builder.setMessage(messageId); - builder.setPositiveButton(R.string.confirm_label, + builder.setPositiveButton(positiveText != 0 ? positiveText : R.string.confirm_label, new DialogInterface.OnClickListener() { @Override @@ -44,7 +56,7 @@ public abstract class ConfirmationDialog { onConfirmButtonPressed(dialog); } }); - builder.setNegativeButton(R.string.cancel_label, + builder.setNegativeButton(negativeText != 0 ? negativeText : R.string.cancel_label, new DialogInterface.OnClickListener() { @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java new file mode 100644 index 000000000..94e186b7a --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java @@ -0,0 +1,24 @@ +package de.danoeh.antennapod.core.event; + +import de.danoeh.antennapod.core.feed.FeedMedia; + +public class FeedMediaEvent { + + public enum Action { + UPDATE + } + + public final Action action; + public final FeedMedia media; + + private FeedMediaEvent(Action action, FeedMedia media) { + this.action = action; + this.media = media; + } + + public static FeedMediaEvent update(FeedMedia media) { + return new FeedMediaEvent(Action.UPDATE, media); + } + + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java new file mode 100644 index 000000000..3769d6bb1 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java @@ -0,0 +1,36 @@ +package de.danoeh.antennapod.core.event; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public class ProgressEvent { + + public enum Action { + START, END + } + + public final Action action; + public final String message; + + private ProgressEvent(Action action, String message) { + this.action = action; + this.message = message; + } + + public static ProgressEvent start(String message) { + return new ProgressEvent(Action.START, message); + } + + public static ProgressEvent end() { + return new ProgressEvent(Action.END, null); + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("action", action) + .append("message", message) + .toString(); + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java index 29ba721fe..2a483ca9b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java @@ -10,7 +10,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import de.danoeh.antennapod.core.asynctask.PicassoImageResource; +import de.danoeh.antennapod.core.asynctask.ImageResource; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.flattr.FlattrStatus; import de.danoeh.antennapod.core.util.flattr.FlattrThing; @@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing; * * @author daniel */ -public class Feed extends FeedFile implements FlattrThing, PicassoImageResource { +public class Feed extends FeedFile implements FlattrThing, ImageResource { public static final int FEEDFILETYPE_FEED = 0; public static final String TYPE_RSS2 = "rss"; public static final String TYPE_RSS091 = "rss"; @@ -167,7 +167,7 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource */ public Feed(String url, Date lastUpdate, String title, String username, String password) { this(url, lastUpdate, title); - preferences = new FeedPreferences(0, true, username, password); + preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, username, password); } @@ -177,10 +177,21 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource */ public boolean hasNewItems() { for (FeedItem item : items) { - if (item.getState() == FeedItem.State.UNREAD) { - if (item.getMedia() != null) { - return true; - } + if (item.isNew()) { + return true; + } + } + return false; + } + + /** + * Returns true if at least one item in the itemlist is unread. + * + */ + public boolean hasUnplayedItems() { + for (FeedItem item : items) { + if (false == item.isNew() && false == item.isPlayed()) { + return true; } } return false; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java index c6f24367e..f77a78721 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java @@ -4,10 +4,10 @@ import android.net.Uri; import java.io.File; -import de.danoeh.antennapod.core.asynctask.PicassoImageResource; +import de.danoeh.antennapod.core.asynctask.ImageResource; -public class FeedImage extends FeedFile implements PicassoImageResource { +public class FeedImage extends FeedFile implements ImageResource { public static final int FEEDFILETYPE_FEEDIMAGE = 1; protected String title; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java index 1168c60e4..9229172f0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.concurrent.Callable; import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.asynctask.PicassoImageResource; +import de.danoeh.antennapod.core.asynctask.ImageResource; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.ShownotesProvider; import de.danoeh.antennapod.core.util.flattr.FlattrStatus; @@ -21,7 +21,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing; * * @author daniel */ -public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, PicassoImageResource { +public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, ImageResource { /** * The id/guid that can be found in the rss/atom feed. Might not be set. @@ -44,7 +44,11 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr private Feed feed; private long feedId; - private boolean read; + private int state; + public final static int NEW = -1; + public final static int UNPLAYED = 0; + public final static int PLAYED = 1; + private String paymentLink; private FlattrStatus flattrStatus; @@ -66,7 +70,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr private boolean autoDownload = true; public FeedItem() { - this.read = true; + this.state = UNPLAYED; this.flattrStatus = new FlattrStatus(); this.hasChapters = false; } @@ -75,7 +79,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr * This constructor is used by DBReader. * */ public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId, - FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, boolean read, + FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state, String itemIdentifier, boolean autoDownload) { this.id = id; this.title = title; @@ -86,7 +90,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr this.flattrStatus = flattrStatus; this.hasChapters = hasChapters; this.image = image; - this.read = read; + this.state = state; this.itemIdentifier = itemIdentifier; this.autoDownload = autoDownload; } @@ -94,13 +98,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr /** * This constructor should be used for creating test objects. */ - public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed) { + public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed) { this.id = id; this.title = title; this.itemIdentifier = itemIdentifier; this.link = link; this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null; - this.read = read; + this.state = state; this.feed = feed; this.flattrStatus = new FlattrStatus(); this.hasChapters = false; @@ -109,13 +113,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr /** * This constructor should be used for creating test objects involving chapter marks. */ - public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed, boolean hasChapters) { + public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed, boolean hasChapters) { this.id = id; this.title = title; this.itemIdentifier = itemIdentifier; this.link = link; this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null; - this.read = read; + this.state = state; this.feed = feed; this.flattrStatus = new FlattrStatus(); this.hasChapters = hasChapters; @@ -238,12 +242,25 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr this.feed = feed; } - public boolean isRead() { - return read; + public boolean isNew() { + return state == NEW; + } + + + public void setNew() { + state = NEW; } - public void setRead(boolean read) { - this.read = read; + public boolean isPlayed() { + return state == PLAYED; + } + + public void setPlayed(boolean played) { + if(played) { + state = PLAYED; + } else { + state = UNPLAYED; + } } private boolean isInProgress() { @@ -257,11 +274,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr public void setContentEncoded(String contentEncoded) { this.contentEncoded = contentEncoded; } - - public void setFlattrStatus(FlattrStatus status) { - this.flattrStatus = status; - } - + public FlattrStatus getFlattrStatus() { return flattrStatus; } @@ -320,7 +333,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr public Uri getImageUri() { if(media != null && media.hasEmbeddedPicture()) { return media.getImageUri(); - } else if (hasItemImageDownloaded()) { + } else if (image != null) { return image.getImageUri(); } else if (feed != null) { return feed.getImageUri(); @@ -342,7 +355,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr return State.IN_PROGRESS; } } - return (isRead() ? State.READ : State.UNREAD); + return (isPlayed() ? State.READ : State.UNREAD); } public long getFeedId() { diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java index 4ad084b39..2fd5666c8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java @@ -61,9 +61,9 @@ public class FeedItemFilter { } List<FeedItem> result = new ArrayList<FeedItem>(); for(FeedItem item : items) { - if(hideUnplayed && false == item.isRead()) continue; + if(hideUnplayed && false == item.isPlayed()) continue; if(hidePaused && item.getState() == FeedItem.State.IN_PROGRESS) continue; - if(hidePlayed && item.isRead()) continue; + if(hidePlayed && item.isPlayed()) continue; boolean isQueued = DBReader.getQueueIDList(context).contains(item.getId()); if(hideQueued && isQueued) continue; if(hideNotQueued && false == isQueued) continue; diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java index ee05020cc..9dbf6cc61 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -28,6 +28,15 @@ public class FeedMedia extends FeedFile implements Playable { public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId"; public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId"; + /** + * Indicates we've checked on the size of the item via the network + * and got an invalid response. Using Integer.MIN_VALUE because + * 1) we'll still check on it in case it gets downloaded (it's <= 0) + * 2) By default all FeedMedia have a size of 0 if we don't know it, + * so this won't conflict with existing practice. + */ + private static final int CHECKED_ON_SIZE_BUT_UNKNOWN = Integer.MIN_VALUE; + private int duration; private int position; // Current position in file private int played_duration; // How many ms of this file have been played (for autoflattring) @@ -35,6 +44,8 @@ public class FeedMedia extends FeedFile implements Playable { private String mime_type; private volatile FeedItem item; private Date playbackCompletionDate; + + // if null: unknown, will be checked private Boolean hasEmbeddedPicture; /* Used for loading item when restoring from parcel. */ @@ -63,6 +74,15 @@ public class FeedMedia extends FeedFile implements Playable { ? null : (Date) playbackCompletionDate.clone(); } + public FeedMedia(long id, FeedItem item, int duration, int position, + long size, String mime_type, String file_url, String download_url, + boolean downloaded, Date playbackCompletionDate, int played_duration, + Boolean hasEmbeddedPicture) { + this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded, + playbackCompletionDate, played_duration); + this.hasEmbeddedPicture = hasEmbeddedPicture; + } + @Override public String getHumanReadableIdentifier() { if (item != null && item.getTitle() != null) { @@ -175,6 +195,9 @@ public class FeedMedia extends FeedFile implements Playable { public void setPosition(int position) { this.position = position; + if(position > 0 && item.isNew()) { + this.item.setPlayed(false); + } } public long getSize() { @@ -185,6 +208,18 @@ public class FeedMedia extends FeedFile implements Playable { this.size = size; } + /** + * Indicates we asked the service what the size was, but didn't + * get a valid answer and we shoudln't check using the network again. + */ + public void setCheckedOnSizeButUnknown() { + this.size = CHECKED_ON_SIZE_BUT_UNKNOWN; + } + + public boolean checkedOnSizeButUnknown() { + return (CHECKED_ON_SIZE_BUT_UNKNOWN == this.size); + } + public String getMime_type() { return mime_type; } @@ -354,14 +389,16 @@ public class FeedMedia extends FeedFile implements Playable { @Override public void saveCurrentPosition(SharedPreferences pref, int newPosition) { - setPosition(newPosition); DBWriter.setFeedMediaPlaybackInformation(ClientConfig.applicationCallbacks.getApplicationInstance(), this); + if(item.isNew()) { + DBWriter.markItemRead(ClientConfig.applicationCallbacks.getApplicationInstance(), false, item.getId()); + } + setPosition(newPosition); } @Override public void onPlaybackStart() { } - @Override public void onPlaybackCompleted() { @@ -429,9 +466,16 @@ public class FeedMedia extends FeedFile implements Playable { } } + public void setHasEmbeddedPicture(Boolean hasEmbeddedPicture) { + this.hasEmbeddedPicture = hasEmbeddedPicture; + } + @Override public void setDownloaded(boolean downloaded) { super.setDownloaded(downloaded); + if(downloaded) { + item.setPlayed(false); + } } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java index 2f0304182..88da865cc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.feed; import android.content.Context; import de.danoeh.antennapod.core.storage.DBWriter; import org.apache.commons.lang3.StringUtils; +import de.danoeh.antennapod.core.preferences.UserPreferences; /** * Contains preferences for a single feed. @@ -11,19 +12,26 @@ public class FeedPreferences { private long feedID; private boolean autoDownload; + public enum AutoDeleteAction { + GLOBAL, + YES, + NO + } + private AutoDeleteAction auto_delete_action; private String username; private String password; - public FeedPreferences(long feedID, boolean autoDownload, String username, String password) { + public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, String username, String password) { this.feedID = feedID; this.autoDownload = autoDownload; + this.auto_delete_action = auto_delete_action; this.username = username; this.password = password; } /** - * Compare another FeedPreferences with this one. The feedID and autoDownload attribute are excluded from the + * Compare another FeedPreferences with this one. The feedID, autoDownload and AutoDeleteAction attribute are excluded from the * comparison. * * @return True if the two objects are different. @@ -41,7 +49,7 @@ public class FeedPreferences { } /** - * Update this FeedPreferences object from another one. The feedID and autoDownload attributes are excluded + * Update this FeedPreferences object from another one. The feedID, autoDownload and AutoDeleteAction attributes are excluded * from the update. */ public void updateFromOther(FeedPreferences other) { @@ -67,6 +75,28 @@ public class FeedPreferences { this.autoDownload = autoDownload; } + public AutoDeleteAction getAutoDeleteAction() { + return auto_delete_action; + } + + public void setAutoDeleteAction(AutoDeleteAction auto_delete_action) { + this.auto_delete_action = auto_delete_action; + } + + public boolean getCurrentAutoDelete() { + switch (auto_delete_action) { + case GLOBAL: + return UserPreferences.isAutoDelete(); + + case YES: + return true; + + case NO: + return false; + } + return false; // TODO - add exceptions here + } + public void save(Context context) { DBWriter.setFeedPreferences(context, this); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java b/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java index c8497f509..97d086e5b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java @@ -8,7 +8,7 @@ import java.util.List; public class QueueEvent { public enum Action { - ADDED, ADDED_ITEMS, REMOVED, CLEARED, DELETED_MEDIA, SORTED, MOVED + ADDED, ADDED_ITEMS, REMOVED, IRREVERSIBLE_REMOVED, CLEARED, DELETED_MEDIA, SORTED, MOVED } public final Action action; diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java new file mode 100644 index 000000000..0baff9723 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java @@ -0,0 +1,33 @@ +package de.danoeh.antennapod.core.glide; + +import android.content.Context; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.load.DecodeFormat; +import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.module.GlideModule; + +import java.io.InputStream; + +import de.danoeh.antennapod.core.preferences.UserPreferences; + +/** + * {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule} + */ +public class ApGlideModule implements GlideModule { + + @Override + public void applyOptions(Context context, GlideBuilder builder) { + builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); + builder.setDiskCache(new InternalCacheDiskCacheFactory(context, + UserPreferences.getImageCacheSize())); + } + + @Override + public void registerComponents(Context context, Glide glide) { + glide.register(GlideUrl.class, InputStream.class, new ApOkHttpUrlLoader.Factory()); + } + +}
\ No newline at end of file diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java new file mode 100644 index 000000000..fc1acd0e1 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java @@ -0,0 +1,11 @@ +package de.danoeh.antennapod.core.glide; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +/** + * The settings that AntennaPod will use for various Glide options + */ +public class ApGlideSettings { + + public static final DiskCacheStrategy AP_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL; +} 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 new file mode 100644 index 000000000..c7ac146b5 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java @@ -0,0 +1,141 @@ +package de.danoeh.antennapod.core.glide; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher; +import com.bumptech.glide.load.data.DataFetcher; +import com.bumptech.glide.load.model.GenericLoaderFactory; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Response; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; + +import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import de.danoeh.antennapod.core.service.download.HttpDownloader; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.NetworkUtils; + +/** + * @see com.bumptech.glide.integration.okhttp.OkHttpUrlLoader + */ +public class ApOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> { + + private static final String TAG = ApOkHttpUrlLoader.class.getSimpleName(); + + /** + * The default factory for {@link ApOkHttpUrlLoader}s. + */ + public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> { + + private static volatile OkHttpClient internalClient; + private OkHttpClient client; + + private static OkHttpClient getInternalClient() { + if (internalClient == null) { + synchronized (Factory.class) { + if (internalClient == null) { + internalClient = AntennapodHttpClient.getHttpClient(); + internalClient.interceptors().add(new NetworkAllowanceInterceptor()); + internalClient.interceptors().add(new BasicAuthenticationInterceptor()); + } + } + } + return internalClient; + } + + /** + * Constructor for a new Factory that runs requests using a static singleton client. + */ + public Factory() { + this(getInternalClient()); + } + + /** + * Constructor for a new Factory that runs requests using given client. + */ + public Factory(OkHttpClient client) { + this.client = client; + } + + @Override + public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) { + return new ApOkHttpUrlLoader(client); + } + + @Override + public void teardown() { + // Do nothing, this instance doesn't own the client. + } + } + + private final OkHttpClient client; + + public ApOkHttpUrlLoader(OkHttpClient client) { + this.client = client; + } + + @Override + public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) { + return new OkHttpStreamFetcher(client, model); + } + + private static class NetworkAllowanceInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + if (NetworkUtils.isDownloadAllowed()) { + return chain.proceed(chain.request()); + } else { + return null; + } + } + + } + + private static class BasicAuthenticationInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + com.squareup.okhttp.Request request = chain.request(); + String url = request.urlString(); + Context context = ClientConfig.applicationCallbacks.getApplicationInstance(); + String authentication = DBReader.getImageAuthentication(context, url); + + if(TextUtils.isEmpty(authentication)) { + Log.d(TAG, "no credentials for '" + url + "'"); + return chain.proceed(request); + } + + // add authentication + String[] auth = authentication.split(":"); + String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1"); + com.squareup.okhttp.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/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java new file mode 100644 index 000000000..ee58c2f39 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java @@ -0,0 +1,267 @@ +package de.danoeh.antennapod.core.glide; + +import android.content.Context; +import android.graphics.Bitmap; +import android.media.ThumbnailUtils; +import android.util.Log; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; + +public class FastBlurTransformation extends BitmapTransformation { + + private static final String TAG = FastBlurTransformation.class.getSimpleName(); + + private static final int STACK_BLUR_RADIUS = 1; + private static final int BLUR_IMAGE_WIDTH = 150; + + public FastBlurTransformation(Context context) { + super(context); + } + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap source, + int outWidth, int outHeight) { + int targetWidth = BLUR_IMAGE_WIDTH; + int targetHeight = (int) (1.0 * outHeight * targetWidth / outWidth); + Bitmap resized = ThumbnailUtils.extractThumbnail(source, targetWidth, targetHeight); + Bitmap result = fastBlur(resized, STACK_BLUR_RADIUS); + if (result == null) { + Log.w(TAG, "result was null"); + return source; + } + return result; + } + + @Override + public String getId() { + return "FastBlurTransformation[width=" + BLUR_IMAGE_WIDTH + "px,radius=" + STACK_BLUR_RADIUS +"]"; + } + + private static Bitmap fastBlur(Bitmap bitmap, int radius) { + + // Stack Blur v1.0 from + // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html + // + // Java Author: Mario Klingemann <mario at quasimondo.com> + // http://incubator.quasimondo.com + // created Feburary 29, 2004 + // Android port : Yahel Bouaziz <yahel at kayenko.com> + // http://www.kayenko.com + // ported april 5th, 2012 + + // This is a compromise between Gaussian Blur and Box blur + // It creates much better looking blurs than Box Blur, but is + // 7x faster than my Gaussian Blur implementation. + // + // I called it Stack Blur because this describes best how this + // filter works internally: it creates a kind of moving stack + // of colors whilst scanning through the image. Thereby it + // just has to add one new block of color to the right side + // of the stack and remove the leftmost color. The remaining + // colors on the topmost layer of the stack are either added on + // or reduced by one, depending on if they are on the right or + // on the left side of the stack. + // + // If you are using this algorithm in your code please add + // the following line: + // + // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> + + if (radius < 1) { + return null; + } + + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int r[] = new int[wh]; + int g[] = new int[wh]; + int b[] = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int vmin[] = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int dv[] = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + return bitmap; + } + +}
\ No newline at end of file diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java index bd6210d13..efe39485e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java @@ -93,7 +93,12 @@ public class GpodnetEpisodeAction { if(StringUtils.isEmpty(podcast) || StringUtils.isEmpty(episode) || StringUtils.isEmpty(actionString)) { return null; } - GpodnetEpisodeAction.Action action = GpodnetEpisodeAction.Action.valueOf(actionString.toUpperCase()); + GpodnetEpisodeAction.Action action; + try { + action = GpodnetEpisodeAction.Action.valueOf(actionString.toUpperCase()); + } catch (IllegalArgumentException e) { + return null; + } String deviceId = object.optString("device", ""); GpodnetEpisodeAction.Builder builder = new GpodnetEpisodeAction.Builder(podcast, episode, action) .deviceId(deviceId); diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java index c3c6ce8c5..1401d5f39 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java @@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -217,26 +216,36 @@ public class GpodnetPreferences { public static void removeRemovedFeeds(Collection<String> removed) { ensurePreferencesLoaded(); + feedListLock.lock(); removedFeeds.removeAll(removed); writePreference(PREF_SYNC_REMOVED, removedFeeds); + feedListLock.unlock(); } - public static synchronized void enqueueEpisodeAction(GpodnetEpisodeAction action) { + public static void enqueueEpisodeAction(GpodnetEpisodeAction action) { ensurePreferencesLoaded(); + feedListLock.lock(); queuedEpisodeActions.add(action); writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions)); + feedListLock.unlock(); GpodnetSyncService.sendSyncActionsIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); } public static List<GpodnetEpisodeAction> getQueuedEpisodeActions() { ensurePreferencesLoaded(); - return Collections.unmodifiableList(queuedEpisodeActions); + List<GpodnetEpisodeAction> copy = new ArrayList(); + feedListLock.lock(); + copy.addAll(queuedEpisodeActions); + feedListLock.unlock(); + return copy; } - public static synchronized void removeQueuedEpisodeActions(Collection<GpodnetEpisodeAction> queued) { + public static void removeQueuedEpisodeActions(Collection<GpodnetEpisodeAction> queued) { ensurePreferencesLoaded(); + feedListLock.lock(); queuedEpisodeActions.removeAll(queued); writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions)); + feedListLock.unlock(); } /** @@ -252,12 +261,14 @@ public class GpodnetPreferences { setUsername(null); setPassword(null); setDeviceID(null); + feedListLock.lock(); addedFeeds.clear(); writePreference(PREF_SYNC_ADDED, addedFeeds); removedFeeds.clear(); writePreference(PREF_SYNC_REMOVED, removedFeeds); queuedEpisodeActions.clear(); writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions)); + feedListLock.unlock(); setLastSubscriptionSyncTimestamp(0); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java index 714f1b051..dfe056f14 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java @@ -3,11 +3,7 @@ package de.danoeh.antennapod.core.preferences; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.util.Log; -import org.apache.commons.lang3.Validate; - -import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.feed.EventDistributor; /** @@ -15,159 +11,104 @@ import de.danoeh.antennapod.core.feed.EventDistributor; * instance of this class must first be instantiated via createInstance() or * otherwise every public method will throw an Exception when called. */ -public class PlaybackPreferences implements - SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = "PlaybackPreferences"; - - /** - * Contains the feed id of the currently playing item if it is a FeedMedia - * object. - */ - public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId"; - - /** - * Contains the id of the currently playing FeedMedia object or - * NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object. - */ - public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId"; - - /** - * Type of the media object that is currently being played. This preference - * is set to NO_MEDIA_PLAYING after playback has been completed and is set - * as soon as the 'play' button is pressed. - */ - public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia"; - - /** True if last played media was streamed. */ - public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream"; - - /** True if last played media was a video. */ - public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo"; - - /** The current player status as int. */ +public class PlaybackPreferences implements SharedPreferences.OnSharedPreferenceChangeListener { + + private static final String TAG = "PlaybackPreferences"; + + /** + * Contains the feed id of the currently playing item if it is a FeedMedia + * object. + */ + public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId"; + + /** + * Contains the id of the currently playing FeedMedia object or + * NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object. + */ + public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId"; + + /** + * Type of the media object that is currently being played. This preference + * is set to NO_MEDIA_PLAYING after playback has been completed and is set + * as soon as the 'play' button is pressed. + */ + public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia"; + + /** + * True if last played media was streamed. + */ + public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream"; + + /** + * True if last played media was a video. + */ + public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo"; + + /** + * The current player status as int. + */ public static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus"; - /** Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */ - public static final long NO_MEDIA_PLAYING = -1; + /** + * Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. + */ + public static final long NO_MEDIA_PLAYING = -1; - /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is playing. */ + /** + * Value of PREF_CURRENT_PLAYER_STATUS if media player status is playing. + */ public static final int PLAYER_STATUS_PLAYING = 1; - /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is paused. */ + /** + * Value of PREF_CURRENT_PLAYER_STATUS if media player status is paused. + */ public static final int PLAYER_STATUS_PAUSED = 2; - /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is neither playing nor paused. */ + /** + * Value of PREF_CURRENT_PLAYER_STATUS if media player status is neither playing nor paused. + */ public static final int PLAYER_STATUS_OTHER = 3; - private long currentlyPlayingFeedId; - private long currentlyPlayingFeedMediaId; - private long currentlyPlayingMedia; - private boolean currentEpisodeIsStream; - private boolean currentEpisodeIsVideo; - private int currentPlayerStatus; - - private static PlaybackPreferences instance; - private Context context; - - private PlaybackPreferences(Context context) { - this.context = context; - loadPreferences(); - } + private static PlaybackPreferences instance; + private static SharedPreferences prefs; - /** - * Sets up the UserPreferences class. - * - * @throws IllegalArgumentException - * if context is null - * */ - public static void createInstance(Context context) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Creating new instance of UserPreferences"); - Validate.notNull(context); - - instance = new PlaybackPreferences(context); - - PreferenceManager.getDefaultSharedPreferences(context) - .registerOnSharedPreferenceChangeListener(instance); - } + private PlaybackPreferences() { + } - private void loadPreferences() { - SharedPreferences sp = PreferenceManager - .getDefaultSharedPreferences(context); - currentlyPlayingFeedId = sp.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1); - currentlyPlayingFeedMediaId = sp.getLong( - PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING); - currentlyPlayingMedia = sp.getLong(PREF_CURRENTLY_PLAYING_MEDIA, - NO_MEDIA_PLAYING); - currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true); - currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false); - currentPlayerStatus = sp.getInt(PREF_CURRENT_PLAYER_STATUS, - PLAYER_STATUS_OTHER); - } + public static void init(Context context) { + instance = new PlaybackPreferences(); + prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.registerOnSharedPreferenceChangeListener(instance); + } - @Override - public void onSharedPreferenceChanged(SharedPreferences sp, String key) { - if (key.equals(PREF_CURRENTLY_PLAYING_FEED_ID)) { - currentlyPlayingFeedId = sp.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, - -1); - - } else if (key.equals(PREF_CURRENTLY_PLAYING_MEDIA)) { - currentlyPlayingMedia = sp - .getLong(PREF_CURRENTLY_PLAYING_MEDIA, -1); - - } else if (key.equals(PREF_CURRENT_EPISODE_IS_STREAM)) { - currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true); - - } else if (key.equals(PREF_CURRENT_EPISODE_IS_VIDEO)) { - currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false); - - } else if (key.equals(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID)) { - currentlyPlayingFeedMediaId = sp.getLong( - PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING); - } - else if (key.equals(PREF_CURRENT_PLAYER_STATUS)) { - currentPlayerStatus = sp.getInt(PREF_CURRENT_PLAYER_STATUS, - PLAYER_STATUS_OTHER); + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals(PREF_CURRENT_PLAYER_STATUS)) { EventDistributor.getInstance().sendPlayerStatusUpdateBroadcast(); } - } - - private static void instanceAvailable() { - if (instance == null) { - throw new IllegalStateException( - "UserPreferences was used before being set up"); - } - } - + } public static long getLastPlayedFeedId() { - instanceAvailable(); - return instance.currentlyPlayingFeedId; + return prefs.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1); } public static long getCurrentlyPlayingMedia() { - instanceAvailable(); - return instance.currentlyPlayingMedia; + return prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING); } public static long getCurrentlyPlayingFeedMediaId() { - return instance.currentlyPlayingFeedMediaId; + return prefs.getLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING); } public static boolean getCurrentEpisodeIsStream() { - instanceAvailable(); - return instance.currentEpisodeIsStream; + return prefs.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true); } public static boolean getCurrentEpisodeIsVideo() { - instanceAvailable(); - return instance.currentEpisodeIsVideo; + return prefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false); } public static int getCurrentPlayerStatus() { - instanceAvailable(); - return instance.currentPlayerStatus; + return prefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER); } - } diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index 594241573..d56829c70 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -5,6 +5,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.os.SystemClock; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; @@ -18,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -29,21 +31,23 @@ import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver; /** * Provides access to preferences set by the user in the settings screen. A * private instance of this class must first be instantiated via - * createInstance() or otherwise every public method will throw an Exception + * init() or otherwise every public method will throw an Exception * when called. */ -public class UserPreferences implements - SharedPreferences.OnSharedPreferenceChangeListener { +public class UserPreferences { public static final String IMPORT_DIR = "import/"; private static final String TAG = "UserPreferences"; - // User Infercasce + // User Interface public static final String PREF_THEME = "prefTheme"; public static final String PREF_HIDDEN_DRAWER_ITEMS = "prefHiddenDrawerItems"; + public static final String PREF_DRAWER_FEED_ORDER = "prefDrawerFeedOrder"; + public static final String PREF_DRAWER_FEED_COUNTER = "prefDrawerFeedCounter"; public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify"; public static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify"; + public static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport"; // Queue public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront"; @@ -54,7 +58,7 @@ public class UserPreferences implements public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue"; public static final String PREF_AUTO_DELETE = "prefAutoDelete"; public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs"; - private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray"; + public static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray"; public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss"; public static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall"; @@ -74,193 +78,42 @@ public class UserPreferences implements // Other public static final String PREF_DATA_FOLDER = "prefDataFolder"; + public static final String PREF_IMAGE_CACHE_SIZE = "prefImageCacheSize"; // Mediaplayer public static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed"; private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs"; private static final String PREF_REWIND_SECS = "prefRewindSecs"; public static final String PREF_QUEUE_LOCKED = "prefQueueLocked"; + public static final String IMAGE_CACHE_DEFAULT_VALUE = "100"; + public static final int IMAGE_CACHE_SIZE_MINIMUM = 20; - // TODO: Make this value configurable - private static final float PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT = 0.8f; - + // Constants private static int EPISODE_CACHE_SIZE_UNLIMITED = -1; + public static int FEED_ORDER_UNPLAYED_EPISODES = 0; + public static int FEED_ORDER_ALPHABETICAL = 1; + public static int FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM = 0; + public static int FEED_COUNTER_SHOW_NEW = 1; + public static int FEED_COUNTER_SHOW_UNPLAYED = 2; + public static int FEED_COUNTER_SHOW_NONE = 3; - private static UserPreferences instance; - private final Context context; - - // User Interface - private int theme; - private List<String> hiddenDrawerItems; - private int notifyPriority; - private boolean persistNotify; - - // Queue - private boolean enqueueAtFront; - - // Playback - private boolean pauseOnHeadsetDisconnect; - private boolean unpauseOnHeadsetReconnect; - private boolean followQueue; - private boolean autoDelete; - private int smartMarkAsPlayedSecs; - private String[] playbackSpeedArray; - private boolean pauseForFocusLoss; - private boolean resumeAfterCall; - - // Network - private long updateInterval; - private boolean allowMobileUpdate; - private int parallelDownloads; - private int episodeCacheSize; - private boolean enableAutodownload; - private boolean enableAutodownloadOnBattery; - private boolean enableAutodownloadWifiFilter; - private String[] autodownloadSelectedNetworks; - - // Services - private boolean autoFlattr; - private float autoFlattrPlayedDurationThreshold; - - // Settings somewhere in the GUI - private String playbackSpeed; - private int fastForwardSecs; - private int rewindSecs; - private boolean queueLocked; - - - private UserPreferences(Context context) { - this.context = context; - loadPreferences(); - } + private static Context context; + private static SharedPreferences prefs; /** * Sets up the UserPreferences class. * * @throws IllegalArgumentException if context is null */ - public static void createInstance(Context context) { + public static void init(Context context) { Log.d(TAG, "Creating new instance of UserPreferences"); Validate.notNull(context); - instance = new UserPreferences(context); + UserPreferences.context = context; + UserPreferences.prefs = PreferenceManager.getDefaultSharedPreferences(context); createImportDirectory(); createNoMediaFile(); - PreferenceManager.getDefaultSharedPreferences(context) - .registerOnSharedPreferenceChangeListener(instance); - - } - - private void loadPreferences() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - - // User Interface - theme = readThemeValue(sp.getString(PREF_THEME, "0")); - if (sp.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) { - notifyPriority = NotificationCompat.PRIORITY_MAX; - } else { - notifyPriority = NotificationCompat.PRIORITY_DEFAULT; - } - hiddenDrawerItems = Arrays.asList(StringUtils.split(sp.getString(PREF_HIDDEN_DRAWER_ITEMS, ""), ',')); - persistNotify = sp.getBoolean(PREF_PERSISTENT_NOTIFICATION, false); - - // Queue - enqueueAtFront = sp.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false); - - // Playback - pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true); - unpauseOnHeadsetReconnect = sp.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true); - followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false); - autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false); - smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30")); - playbackSpeedArray = readPlaybackSpeedArray(sp.getString( - PREF_PLAYBACK_SPEED_ARRAY, null)); - pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false); - - // Network - updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL, "0")); - allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false); - parallelDownloads = Integer.valueOf(sp.getString(PREF_PARALLEL_DOWNLOADS, "6")); - EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger( - R.integer.episode_cache_size_unlimited); - episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(PREF_EPISODE_CACHE_SIZE, "20")); - enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); - enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true); - enableAutodownloadWifiFilter = sp.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false); - autodownloadSelectedNetworks = StringUtils.split( - sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ','); - - // Services - autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false); - autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, - PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT); - - // MediaPlayer - playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0"); - fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30); - rewindSecs = sp.getInt(PREF_REWIND_SECS, 30); - queueLocked = sp.getBoolean(PREF_QUEUE_LOCKED, false); - } - - private int readThemeValue(String valueFromPrefs) { - switch (Integer.parseInt(valueFromPrefs)) { - case 0: - return R.style.Theme_AntennaPod_Light; - case 1: - return R.style.Theme_AntennaPod_Dark; - default: - return R.style.Theme_AntennaPod_Light; - } - } - - private long readUpdateInterval(String valueFromPrefs) { - int hours = Integer.parseInt(valueFromPrefs); - return TimeUnit.HOURS.toMillis(hours); - } - - private int readEpisodeCacheSizeInternal(String valueFromPrefs) { - if (valueFromPrefs.equals(context - .getString(R.string.pref_episode_cache_unlimited))) { - return EPISODE_CACHE_SIZE_UNLIMITED; - } else { - return Integer.valueOf(valueFromPrefs); - } - } - - private String[] readPlaybackSpeedArray(String valueFromPrefs) { - String[] selectedSpeeds = null; - // If this preference hasn't been set yet, return the default options - if (valueFromPrefs == null) { - String[] allSpeeds = context.getResources().getStringArray( - R.array.playback_speed_values); - List<String> speedList = new LinkedList<String>(); - for (String speedStr : allSpeeds) { - float speed = Float.parseFloat(speedStr); - if (speed < 2.0001 && speed * 10 % 1 == 0) { - speedList.add(speedStr); - } - } - selectedSpeeds = speedList.toArray(new String[speedList.size()]); - } else { - try { - JSONArray jsonArray = new JSONArray(valueFromPrefs); - selectedSpeeds = new String[jsonArray.length()]; - for (int i = 0; i < jsonArray.length(); i++) { - selectedSpeeds[i] = jsonArray.getString(i); - } - } catch (JSONException e) { - Log.e(TAG, "Got JSON error when trying to get speeds from JSONArray"); - e.printStackTrace(); - } - } - return selectedSpeeds; - } - - private static void instanceAvailable() { - if (instance == null) { - throw new IllegalStateException("UserPreferences was used before being set up"); - } } /** @@ -269,8 +122,7 @@ public class UserPreferences implements * @return R.style.Theme_AntennaPod_Light or R.style.Theme_AntennaPod_Dark */ public static int getTheme() { - instanceAvailable(); - return instance.theme; + return readThemeValue(prefs.getString(PREF_THEME, "0")); } public static int getNoTitleTheme() { @@ -283,8 +135,18 @@ public class UserPreferences implements } public static List<String> getHiddenDrawerItems() { - instanceAvailable(); - return new ArrayList<String>(instance.hiddenDrawerItems); + String hiddenItems = prefs.getString(PREF_HIDDEN_DRAWER_ITEMS, ""); + return new ArrayList<String>(Arrays.asList(StringUtils.split(hiddenItems, ','))); + } + + public static int getFeedOrder() { + String value = prefs.getString(PREF_DRAWER_FEED_ORDER, "0"); + return Integer.valueOf(value); + } + + public static int getFeedCounter() { + String value = prefs.getString(PREF_DRAWER_FEED_COUNTER, "0"); + return Integer.valueOf(value); } /** @@ -293,8 +155,11 @@ public class UserPreferences implements * @return NotificationCompat.PRIORITY_MAX or NotificationCompat.PRIORITY_DEFAULT */ public static int getNotifyPriority() { - instanceAvailable(); - return instance.notifyPriority; + if (prefs.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) { + return NotificationCompat.PRIORITY_MAX; + } else { + return NotificationCompat.PRIORITY_DEFAULT; + } } /** @@ -303,8 +168,16 @@ public class UserPreferences implements * @return {@code true} if notifications are persistent, {@code false} otherwise */ public static boolean isPersistNotify() { - instanceAvailable(); - return instance.persistNotify; + return prefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, false); + } + + /** + * Returns true if download reports are shown + * + * @return {@code true} if download reports are shown, {@code false} otherwise + */ + public static boolean showDownloadReport() { + return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true); } /** @@ -313,73 +186,77 @@ public class UserPreferences implements * @return {@code true} if new queue elements are added to the front; {@code false} otherwise */ public static boolean enqueueAtFront() { - instanceAvailable(); - return instance.enqueueAtFront; + return prefs.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false); } public static boolean isPauseOnHeadsetDisconnect() { - instanceAvailable(); - return instance.pauseOnHeadsetDisconnect; + return prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true); } public static boolean isUnpauseOnHeadsetReconnect() { - instanceAvailable(); - return instance.unpauseOnHeadsetReconnect; + return prefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true); } public static boolean isFollowQueue() { - instanceAvailable(); - return instance.followQueue; + return prefs.getBoolean(PREF_FOLLOW_QUEUE, false); } public static boolean isAutoDelete() { - instanceAvailable(); - return instance.autoDelete; + return prefs.getBoolean(PREF_AUTO_DELETE, false); } public static int getSmartMarkAsPlayedSecs() { - instanceAvailable(); - return instance.smartMarkAsPlayedSecs; + return Integer.valueOf(prefs.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30")); } public static boolean isAutoFlattr() { - instanceAvailable(); - return instance.autoFlattr; + return prefs.getBoolean(PREF_AUTO_FLATTR, false); } public static String getPlaybackSpeed() { - instanceAvailable(); - return instance.playbackSpeed; + return prefs.getString(PREF_PLAYBACK_SPEED, "1.0"); } public static String[] getPlaybackSpeedArray() { - instanceAvailable(); - return instance.playbackSpeedArray; + return readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null)); } public static boolean shouldPauseForFocusLoss() { - instanceAvailable(); - return instance.pauseForFocusLoss; + return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false); } public static long getUpdateInterval() { - instanceAvailable(); - return instance.updateInterval; + String updateInterval = prefs.getString(PREF_UPDATE_INTERVAL, "0"); + if(false == updateInterval.contains(":")) { + return readUpdateInterval(updateInterval); + } else { + return 0; + } + } + + public static int[] getUpdateTimeOfDay() { + String datetime = prefs.getString(PREF_UPDATE_INTERVAL, ""); + if(datetime.length() >= 3 && datetime.contains(":")) { + String[] parts = datetime.split(":"); + int hourOfDay = Integer.valueOf(parts[0]); + int minute = Integer.valueOf(parts[1]); + return new int[] { hourOfDay, minute }; + } else { + return new int[0]; + } } public static boolean isAllowMobileUpdate() { - instanceAvailable(); - return instance.allowMobileUpdate; + return prefs.getBoolean(PREF_MOBILE_UPDATE, false); } public static int getParallelDownloads() { - instanceAvailable(); - return instance.parallelDownloads; + return Integer.valueOf(prefs.getString(PREF_PARALLEL_DOWNLOADS, "4")); } public static int getEpisodeCacheSizeUnlimited() { - return EPISODE_CACHE_SIZE_UNLIMITED; + return context.getResources().getInteger(R.integer.episode_cache_size_unlimited); } /** @@ -388,33 +265,40 @@ public class UserPreferences implements * 'unlimited'. */ public static int getEpisodeCacheSize() { - instanceAvailable(); - return instance.episodeCacheSize; + return readEpisodeCacheSizeInternal(prefs.getString(PREF_EPISODE_CACHE_SIZE, "20")); } public static boolean isEnableAutodownload() { - instanceAvailable(); - return instance.enableAutodownload; + return prefs.getBoolean(PREF_ENABLE_AUTODL, false); } public static boolean isEnableAutodownloadOnBattery() { - instanceAvailable(); - return instance.enableAutodownloadOnBattery; + return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true); } public static boolean isEnableAutodownloadWifiFilter() { - instanceAvailable(); - return instance.enableAutodownloadWifiFilter; + return prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false); + } + + public static int getImageCacheSize() { + String cacheSizeString = prefs.getString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE); + int cacheSizeInt = Integer.valueOf(cacheSizeString); + // if the cache size is too small the user won't get any images at all + // that's bad, force it back to the default. + if (cacheSizeInt < IMAGE_CACHE_SIZE_MINIMUM) { + prefs.edit().putString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE).apply(); + cacheSizeInt = Integer.valueOf(IMAGE_CACHE_DEFAULT_VALUE); + } + int cacheSizeMB = cacheSizeInt * 1024 * 1024; + return cacheSizeMB; } public static int getFastFowardSecs() { - instanceAvailable(); - return instance.fastForwardSecs; + return prefs.getInt(PREF_FAST_FORWARD_SECS, 30); } public static int getRewindSecs() { - instanceAvailable(); - return instance.rewindSecs; + return prefs.getInt(PREF_REWIND_SECS, 30); } @@ -423,145 +307,38 @@ public class UserPreferences implements * duration. */ public static float getAutoFlattrPlayedDurationThreshold() { - instanceAvailable(); - return instance.autoFlattrPlayedDurationThreshold; + return prefs.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, 0.8f); } public static String[] getAutodownloadSelectedNetworks() { - instanceAvailable(); - return instance.autodownloadSelectedNetworks; + String selectedNetWorks = prefs.getString(PREF_AUTODL_SELECTED_NETWORKS, ""); + return StringUtils.split(selectedNetWorks, ','); } public static boolean shouldResumeAfterCall() { - instanceAvailable(); - return instance.resumeAfterCall; + return prefs.getBoolean(PREF_RESUME_AFTER_CALL, true); } public static boolean isQueueLocked() { - instanceAvailable(); - return instance.queueLocked; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sp, String key) { - Log.d(TAG, "Registered change of user preferences. Key: " + key); - switch(key) { - // User Interface - case PREF_THEME: - theme = readThemeValue(sp.getString(PREF_THEME, "")); - break; - case PREF_HIDDEN_DRAWER_ITEMS: - hiddenDrawerItems = Arrays.asList(StringUtils.split(sp.getString(PREF_HIDDEN_DRAWER_ITEMS, ""), ',')); - break; - case PREF_EXPANDED_NOTIFICATION: - if (sp.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) { - notifyPriority = NotificationCompat.PRIORITY_MAX; - } else { - notifyPriority = NotificationCompat.PRIORITY_DEFAULT; - } - break; - case PREF_PERSISTENT_NOTIFICATION: - persistNotify = sp.getBoolean(PREF_PERSISTENT_NOTIFICATION, false); - break; - // Queue - case PREF_QUEUE_ADD_TO_FRONT: - enqueueAtFront = sp.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false); - break; - // Playback - case PREF_PAUSE_ON_HEADSET_DISCONNECT: - pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true); - break; - case PREF_UNPAUSE_ON_HEADSET_RECONNECT: - unpauseOnHeadsetReconnect = sp.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true); - break; - case PREF_FOLLOW_QUEUE: - followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false); - break; - case PREF_AUTO_DELETE: - autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false); - break; - case PREF_SMART_MARK_AS_PLAYED_SECS: - smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30")); - break; - case PREF_PLAYBACK_SPEED_ARRAY: - playbackSpeedArray = readPlaybackSpeedArray(sp.getString(PREF_PLAYBACK_SPEED_ARRAY, null)); - break; - case PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS: - pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false); - break; - case PREF_RESUME_AFTER_CALL: - resumeAfterCall = sp.getBoolean(PREF_RESUME_AFTER_CALL, true); - break; - // Network - case PREF_UPDATE_INTERVAL: - updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL, "0")); - ClientConfig.applicationCallbacks.setUpdateInterval(updateInterval); - break; - case PREF_MOBILE_UPDATE: - allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false); - break; - case PREF_PARALLEL_DOWNLOADS: - parallelDownloads = Integer.valueOf(sp.getString(PREF_PARALLEL_DOWNLOADS, "6")); - break; - case PREF_EPISODE_CACHE_SIZE: - episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(PREF_EPISODE_CACHE_SIZE, "20")); - break; - case PREF_ENABLE_AUTODL: - enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false); - break; - case PREF_ENABLE_AUTODL_ON_BATTERY: - enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true); - break; - case PREF_ENABLE_AUTODL_WIFI_FILTER: - enableAutodownloadWifiFilter = sp.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false); - break; - case PREF_AUTODL_SELECTED_NETWORKS: - autodownloadSelectedNetworks = StringUtils.split( - sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ','); - break; - // Services - case PREF_AUTO_FLATTR: - autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false); - break; - case PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD: - autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, - PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT); - break; - // Mediaplayer - case PREF_PLAYBACK_SPEED: - playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0"); - break; - case PREF_FAST_FORWARD_SECS: - fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30); - break; - case PREF_REWIND_SECS: - rewindSecs = sp.getInt(PREF_REWIND_SECS, 30); - break; - case PREF_QUEUE_LOCKED: - queueLocked = sp.getBoolean(PREF_QUEUE_LOCKED, false); - break; - default: - Log.w(TAG, "Unhandled key: " + key); - } + return prefs.getBoolean(PREF_QUEUE_LOCKED, false); } public static void setPrefFastForwardSecs(int secs) { - Log.d(TAG, "setPrefFastForwardSecs(" + secs +")"); - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit(); - editor.putInt(PREF_FAST_FORWARD_SECS, secs); - editor.commit(); + prefs.edit() + .putInt(PREF_FAST_FORWARD_SECS, secs) + .apply(); } public static void setPrefRewindSecs(int secs) { - Log.d(TAG, "setPrefRewindSecs(" + secs +")"); - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit(); - editor.putInt(PREF_REWIND_SECS, secs); - editor.commit(); + prefs.edit() + .putInt(PREF_REWIND_SECS, secs) + .apply(); } public static void setPlaybackSpeed(String speed) { - PreferenceManager.getDefaultSharedPreferences(instance.context).edit() - .putString(PREF_PLAYBACK_SPEED, speed).apply(); + prefs.edit() + .putString(PREF_PLAYBACK_SPEED, speed) + .apply(); } public static void setPlaybackSpeedArray(String[] speeds) { @@ -569,72 +346,117 @@ public class UserPreferences implements for (String speed : speeds) { jsonArray.put(speed); } - PreferenceManager.getDefaultSharedPreferences(instance.context).edit() - .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()) - .apply(); + prefs.edit() + .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()) + .apply(); + } + + public static void setAutodownloadSelectedNetworks(String[] value) { + prefs.edit() + .putString(PREF_AUTODL_SELECTED_NETWORKS, StringUtils.join(value, ',')) + .apply(); } - public static void setAutodownloadSelectedNetworks(Context context, - String[] value) { - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()) - .edit(); - editor.putString(PREF_AUTODL_SELECTED_NETWORKS, - StringUtils.join(value, ',')); - editor.commit(); + /** + * Sets the update interval value. + */ + public static void setUpdateInterval(long hours) { + prefs.edit() + .putString(PREF_UPDATE_INTERVAL, String.valueOf(hours)) + .apply(); + // when updating with an interval, we assume the user wants + // to update *now* and then every 'hours' interval thereafter. + restartUpdateAlarm(true); } /** - * Sets the update interval value. Should only be used for testing purposes! + * Sets the update interval value. */ - public static void setUpdateInterval(Context context, long newValue) { - instanceAvailable(); - SharedPreferences.Editor editor = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()) - .edit(); - editor.putString(PREF_UPDATE_INTERVAL, - String.valueOf(newValue)); - editor.commit(); - instance.updateInterval = newValue; + public static void setUpdateTimeOfDay(int hourOfDay, int minute) { + prefs.edit() + .putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute) + .apply(); + restartUpdateAlarm(false); } /** * Change the auto-flattr settings * - * @param context For accessing the shared preferences * @param enabled Whether automatic flattring should be enabled at all * @param autoFlattrThreshold The percentage of playback time after which an episode should be * flattrd. Must be a value between 0 and 1 (inclusive) * */ - public static void setAutoFlattrSettings(Context context, boolean enabled, float autoFlattrThreshold) { - instanceAvailable(); + public static void setAutoFlattrSettings( boolean enabled, float autoFlattrThreshold) { Validate.inclusiveBetween(0.0, 1.0, autoFlattrThreshold); - PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()) - .edit() - .putBoolean(PREF_AUTO_FLATTR, enabled) - .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold) - .commit(); - instance.autoFlattr = enabled; - instance.autoFlattrPlayedDurationThreshold = autoFlattrThreshold; + prefs.edit() + .putBoolean(PREF_AUTO_FLATTR, enabled) + .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold) + .apply(); } public static void setHiddenDrawerItems(Context context, List<String> items) { - instanceAvailable(); - instance.hiddenDrawerItems = items; String str = StringUtils.join(items, ','); - PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()) - .edit() - .putString(PREF_HIDDEN_DRAWER_ITEMS, str) - .commit(); + prefs.edit() + .putString(PREF_HIDDEN_DRAWER_ITEMS, str) + .apply(); } public static void setQueueLocked(boolean locked) { - instanceAvailable(); - instance.queueLocked = locked; - PreferenceManager.getDefaultSharedPreferences(instance.context) - .edit() - .putBoolean(PREF_QUEUE_LOCKED, locked) - .commit(); + prefs.edit() + .putBoolean(PREF_QUEUE_LOCKED, locked) + .apply(); + } + + private static int readThemeValue(String valueFromPrefs) { + switch (Integer.parseInt(valueFromPrefs)) { + case 0: + return R.style.Theme_AntennaPod_Light; + case 1: + return R.style.Theme_AntennaPod_Dark; + default: + return R.style.Theme_AntennaPod_Light; + } + } + + private static long readUpdateInterval(String valueFromPrefs) { + int hours = Integer.parseInt(valueFromPrefs); + return TimeUnit.HOURS.toMillis(hours); + } + + private static int readEpisodeCacheSizeInternal(String valueFromPrefs) { + if (valueFromPrefs.equals(context.getString(R.string.pref_episode_cache_unlimited))) { + return EPISODE_CACHE_SIZE_UNLIMITED; + } else { + return Integer.valueOf(valueFromPrefs); + } + } + + private static String[] readPlaybackSpeedArray(String valueFromPrefs) { + String[] selectedSpeeds = null; + // If this preference hasn't been set yet, return the default options + if (valueFromPrefs == null) { + String[] allSpeeds = context.getResources().getStringArray(R.array.playback_speed_values); + List<String> speedList = new LinkedList<String>(); + for (String speedStr : allSpeeds) { + float speed = Float.parseFloat(speedStr); + if (speed < 2.0001 && speed * 10 % 1 == 0) { + speedList.add(speedStr); + } + } + selectedSpeeds = speedList.toArray(new String[speedList.size()]); + } else { + try { + JSONArray jsonArray = new JSONArray(valueFromPrefs); + selectedSpeeds = new String[jsonArray.length()]; + for (int i = 0; i < jsonArray.length(); i++) { + selectedSpeeds[i] = jsonArray.getString(i); + } + } catch (JSONException e) { + Log.e(TAG, "Got JSON error when trying to get speeds from JSONArray"); + e.printStackTrace(); + } + } + return selectedSpeeds; } @@ -648,9 +470,6 @@ public class UserPreferences implements * could not be created. */ public static File getDataFolder(Context context, String type) { - instanceAvailable(); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(context.getApplicationContext()); String strDir = prefs.getString(PREF_DATA_FOLDER, null); if (strDir == null) { Log.d(TAG, "Using default data folder"); @@ -696,12 +515,9 @@ public class UserPreferences implements public static void setDataFolder(String dir) { Log.d(TAG, "Result from DirectoryChooser: " + dir); - instanceAvailable(); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(instance.context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(PREF_DATA_FOLDER, dir); - editor.commit(); + prefs.edit() + .putString(PREF_DATA_FOLDER, dir) + .apply(); createImportDirectory(); } @@ -709,8 +525,7 @@ public class UserPreferences implements * Create a .nomedia file to prevent scanning by the media scanner. */ private static void createNoMediaFile() { - File f = new File(instance.context.getExternalFilesDir(null), - ".nomedia"); + File f = new File(context.getExternalFilesDir(null), ".nomedia"); if (!f.exists()) { try { f.createNewFile(); @@ -727,8 +542,7 @@ public class UserPreferences implements * available */ private static void createImportDirectory() { - File importDir = getDataFolder(instance.context, - IMPORT_DIR); + File importDir = getDataFolder(context, IMPORT_DIR); if (importDir != null) { if (importDir.exists()) { Log.d(TAG, "Import directory already exists"); @@ -741,32 +555,69 @@ public class UserPreferences implements } } + public static void restartUpdateAlarm(boolean now) { + int[] timeOfDay = getUpdateTimeOfDay(); + Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay)); + if (timeOfDay.length == 2) { + restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]); + } else { + long hours = getUpdateInterval(); + long startTrigger = hours; + if (now) { + startTrigger = TimeUnit.SECONDS.toMillis(10); + } + restartUpdateIntervalAlarm(startTrigger, hours); + } + } + /** - * Updates alarm registered with the AlarmManager service or deactivates it. + * Sets the interval in which the feeds are refreshed automatically */ - public static void restartUpdateAlarm(long triggerAtMillis, long intervalMillis) { - instanceAvailable(); + public static void restartUpdateIntervalAlarm(long triggerAtMillis, long intervalMillis) { Log.d(TAG, "Restarting update alarm."); - AlarmManager alarmManager = (AlarmManager) instance.context - .getSystemService(Context.ALARM_SERVICE); - PendingIntent updateIntent = PendingIntent.getBroadcast( - instance.context, 0, new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0, + new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0); alarmManager.cancel(updateIntent); - if (intervalMillis != 0) { - alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis, + if (intervalMillis > 0) { + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + triggerAtMillis, updateIntent); - Log.d(TAG, "Changed alarm to new interval"); + Log.d(TAG, "Changed alarm to new interval " + TimeUnit.MILLISECONDS.toHours(intervalMillis) + " h"); } else { Log.d(TAG, "Automatic update was deactivated"); } } + /** + * Sets time of day the feeds are refreshed automatically + */ + public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) { + Log.d(TAG, "Restarting update alarm."); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0, + new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0); + alarmManager.cancel(updateIntent); + + Calendar now = Calendar.getInstance(); + Calendar alarm = (Calendar)now.clone(); + alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay); + alarm.set(Calendar.MINUTE, minute); + if (alarm.before(now) || alarm.equals(now)) { + alarm.add(Calendar.DATE, 1); + } + Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis()); + alarmManager.set(AlarmManager.RTC_WAKEUP, + alarm.getTimeInMillis(), + updateIntent); + Log.d(TAG, "Changed alarm to new time of day " + hoursOfDay + ":" + minute); + } /** * Reads episode cache size as it is saved in the episode_cache_size_values array. */ public static int readEpisodeCacheSize(String valueFromPrefs) { - instanceAvailable(); - return instance.readEpisodeCacheSizeInternal(valueFromPrefs); + return readEpisodeCacheSizeInternal(valueFromPrefs); } + } diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java index 84277b6d5..7fa92f30c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java @@ -7,28 +7,25 @@ import android.util.Log; import org.apache.commons.lang3.StringUtils; -import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; /** Listens for events that make it necessary to reset the update alarm. */ public class AlarmUpdateReceiver extends BroadcastReceiver { + private static final String TAG = "AlarmUpdateReceiver"; @Override public void onReceive(Context context, Intent intent) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Received intent"); + Log.d(TAG, "Received intent"); if (StringUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Resetting update alarm after reboot"); + Log.d(TAG, "Resetting update alarm after reboot"); } else if (StringUtils.equals(intent.getAction(), Intent.ACTION_PACKAGE_REPLACED)) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Resetting update alarm after app upgrade"); + Log.d(TAG, "Resetting update alarm after app upgrade"); } - - ClientConfig.applicationCallbacks.setUpdateInterval(UserPreferences.getUpdateInterval()); - + PlaybackPreferences.init(context); + UserPreferences.init(context); + UserPreferences.restartUpdateAlarm(false); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java index d37f97a5f..b959c7301 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java +++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.util.Log; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.util.NetworkUtils; @@ -18,11 +19,12 @@ public class FeedUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "Received intent"); - if (NetworkUtils.isDownloadAllowed(context)) { - DBTasks.refreshExpiredFeeds(context); + if (NetworkUtils.isDownloadAllowed()) { + DBTasks.refreshAllFeeds(context, null); } else { Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed"); } + UserPreferences.restartUpdateAlarm(false); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java new file mode 100644 index 000000000..2f6e67a28 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java @@ -0,0 +1,76 @@ +package de.danoeh.antennapod.core.service; + +import android.app.IntentService; +import android.content.Intent; +import android.util.Log; + +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; + +import de.danoeh.antennapod.core.event.FeedMediaEvent; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.NetworkUtils; +import de.greenrobot.event.EventBus; + +public class FeedMediaSizeService extends IntentService { + + private final static String TAG = "FeedMediaSizeService"; + + public FeedMediaSizeService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.d(TAG, "onHandleIntent()"); + if(false == NetworkUtils.isDownloadAllowed()) { + return; + } + List<FeedMedia> list = DBReader.getFeedMediaUnknownSize(this); + for (FeedMedia media : list) { + Log.d(TAG, "Getting size currently " + media.getSize() + " for " + media.getDownload_url()); + if(false == NetworkUtils.isDownloadAllowed()) { + return; + } + long size = Integer.MIN_VALUE; + if (media.isDownloaded()) { + File mediaFile = new File(media.getLocalMediaUrl()); + if(mediaFile.exists()) { + size = mediaFile.length(); + } + } else if (false == media.checkedOnSizeButUnknown()) { + // only query the network if we haven't already checked + HttpURLConnection conn = null; + try { + URL url = new URL(media.getDownload_url()); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestProperty("Accept-Encoding", ""); + conn.setRequestMethod("HEAD"); + size = conn.getContentLength(); + } catch (IOException e) { + Log.d(TAG, media.getDownload_url()); + e.printStackTrace(); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + if (size <= 0) { + // they didn't tell us the size, but we don't want to keep querying on it + media.setCheckedOnSizeButUnknown(); + } else { + media.setSize(size); + } + Log.d(TAG, "Size now: " + media.getSize()); + DBWriter.setFeedMedia(this, media); + EventBus.getDefault().post(FeedMediaEvent.update(media)); + } + } + +} 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 3f2222f42..b80b4303f 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 @@ -108,7 +108,7 @@ public class GpodnetSyncService extends Service { private synchronized void sync() { - if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable(this) == false) { + if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable() == false) { stopSelf(); return; } 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 e7b226eca..4fdaf6843 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 @@ -243,7 +243,7 @@ public class DownloadService extends Service { handler = new Handler(); newMediaFiles = Collections.synchronizedList(new ArrayList<Long>()); reportQueue = Collections.synchronizedList(new ArrayList<DownloadStatus>()); - downloads = new ArrayList<Downloader>(); + downloads = Collections.synchronizedList(new ArrayList<Downloader>()); numberOfDownloads = new AtomicInteger(0); IntentFilter cancelDownloadReceiverFilter = new IntentFilter(); @@ -309,7 +309,8 @@ public class DownloadService extends Service { Log.d(TAG, "Service shutting down"); isRunning = false; - if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()) { + if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() && + UserPreferences.showDownloadReport()) { updateReport(); } @@ -324,7 +325,13 @@ public class DownloadService extends Service { cancelNotificationUpdater(); unregisterReceiver(cancelDownloadReceiver); + // TODO: I'm not sure this is actually needed. + // We could just invoke the autodownloadUndownloadeditems method + // and it would get everything it's supposed to. By sending it the + // items in newMediaFiles we're overriding the download algorithm, + // which is not something we should probably do. if (!newMediaFiles.isEmpty()) { + Log.d(TAG, "newMediaFiles exist, autodownload them"); DBTasks.autodownloadUndownloadedItems(getApplicationContext(), ArrayUtils.toPrimitive(newMediaFiles.toArray(new Long[newMediaFiles.size()]))); } @@ -434,6 +441,7 @@ public class DownloadService extends Service { } else { Log.e(TAG, "Could not cancel download with url " + url); } + sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED)); } else if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) { for (Downloader d : downloads) { @@ -774,52 +782,18 @@ public class DownloadService extends Service { for (int i = 0; i < savedFeeds.length; i++) { Feed savedFeed = savedFeeds[i]; - // Download Feed Image if provided and not downloaded - if (savedFeed.getImage() != null - && savedFeed.getImage().isDownloaded() == false) { - Log.d(TAG, "Feed has image; Downloading...."); - savedFeed.getImage().setOwner(savedFeed); - final Feed savedFeedRef = savedFeed; - try { - requester.downloadImage(DownloadService.this, - savedFeedRef.getImage()); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DBWriter.addDownloadStatus( - DownloadService.this, - new DownloadStatus( - savedFeedRef.getImage(), - savedFeedRef - .getImage() - .getHumanReadableIdentifier(), - DownloadError.ERROR_REQUEST_ERROR, - false, e.getMessage() - ) - ); - } - } // queue new media files for automatic download for (FeedItem item : savedFeed.getItems()) { if(item.getPubDate() == null) { Log.d(TAG, item.toString()); } - if(item.getImage() != null && item.getImage().isDownloaded() == false) { - item.getImage().setOwner(item); - try { - requester.downloadImage(DownloadService.this, - item.getImage()); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - } - if (!item.isRead() && item.hasMedia() && !item.getMedia().isDownloaded()) { + if (item.isNew() && item.hasMedia() && !item.getMedia().isDownloaded()) { newMediaFiles.add(item.getMedia().getId()); } } // If loadAllPages=true, check if another page is available and queue it for download - final boolean loadAllPages = results.get(i).first.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES); final Feed feed = results.get(i).second.feed; if (loadAllPages && feed.getNextPageLink() != null) { @@ -889,7 +863,7 @@ public class DownloadService extends Service { feed.setFile_url(request.getDestination()); feed.setId(request.getFeedfileId()); feed.setDownloaded(true); - feed.setPreferences(new FeedPreferences(0, true, + feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, request.getUsername(), request.getPassword())); feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0)); @@ -1143,6 +1117,7 @@ public class DownloadService extends Service { boolean chaptersRead = false; media.setDownloaded(true); media.setFile_url(request.getDestination()); + media.setHasEmbeddedPicture(null); // Get duration MediaMetadataRetriever mmr = null; @@ -1170,12 +1145,16 @@ public class DownloadService extends Service { } try { - if (chaptersRead) { - DBWriter.setFeedItem(DownloadService.this, media.getItem()).get(); - } + // we've received the media, we don't want to autodownload it again + FeedItem item = media.getItem(); + item.setAutoDownload(false); + + // update the db + DBWriter.setFeedItem(DownloadService.this, item).get(); + DBWriter.setFeedMedia(DownloadService.this, media).get(); - if (!DBTasks.isInQueue(DownloadService.this, media.getItem().getId())) { - DBWriter.addQueueItem(DownloadService.this, media.getItem().getId()).get(); + if (!DBTasks.isInQueue(DownloadService.this, item.getId())) { + DBWriter.addQueueItem(DownloadService.this, item.getId()).get(); } } catch (ExecutionException e) { e.printStackTrace(); @@ -1240,7 +1219,16 @@ public class DownloadService extends Service { } public List<Downloader> getDownloads() { - return downloads; + if (downloads == null) { + // this is unusual, but it should be OK, we'll return + // an empty list to make it easy for people + return new ArrayList<Downloader>(); + } + + // return a copy of downloads, but the copy doesn't need to be synchronized. + synchronized (downloads) { + return new ArrayList<Downloader>(downloads); + } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index 3f6769ee4..7e3f27856 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -29,12 +29,16 @@ import android.view.KeyEvent; import android.view.SurfaceHolder; import android.widget.Toast; -import com.squareup.picasso.Picasso; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.SimpleTarget; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.List; +import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; @@ -42,6 +46,7 @@ import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; @@ -442,7 +447,7 @@ public class PlaybackService extends Service { } writePlayerStatusPlaybackPreferences(); - final Playable playable = mediaPlayer.getPSMPInfo().playable; + final Playable playable = newInfo.playable; // Gpodder: send play action if(GpodnetPreferences.loggedIn() && playable instanceof FeedMedia) { @@ -525,7 +530,7 @@ public class PlaybackService extends Service { public boolean onMediaPlayerError(Object inObj, int what, int extra) { final String TAG = "PlaybackService.onErrorListener"; Log.w(TAG, "An error has occured: " + what + " " + extra); - if (mediaPlayer.getPSMPInfo().playerStatus == PlayerStatus.PLAYING) { + if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) { mediaPlayer.pause(true, false); } sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what); @@ -549,7 +554,7 @@ public class PlaybackService extends Service { private void endPlayback(boolean playNextEpisode) { Log.d(TAG, "Playback ended"); - final Playable playable = mediaPlayer.getPSMPInfo().playable; + final Playable playable = mediaPlayer.getPlayable(); if (playable == null) { Log.e(TAG, "Cannot end playback: media was null"); return; @@ -584,7 +589,7 @@ public class PlaybackService extends Service { } // Delete episode if enabled - if(UserPreferences.isAutoDelete()) { + if(item.getFeed().getPreferences().getCurrentAutoDelete()) { DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId()); Log.d(TAG, "Episode Deleted"); } @@ -634,7 +639,7 @@ public class PlaybackService extends Service { writePlaybackPreferencesNoMediaPlaying(); if (nextMedia != null) { - stream = !playable.localFileAvailable(); + stream = !nextMedia.localFileAvailable(); mediaPlayer.playMediaObject(nextMedia, stream, startWhenPrepared, prepareImmediately); sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, (nextMedia.getMediaType() == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO); @@ -744,8 +749,7 @@ public class PlaybackService extends Service { SharedPreferences.Editor editor = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()).edit(); - PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo(); - int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus); + int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus()); editor.putInt( PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus); @@ -770,7 +774,7 @@ public class PlaybackService extends Service { /** * Used by setupNotification to load notification data in another thread. */ - private AsyncTask<Void, Void, Void> notificationSetupTask; + private Thread notificationSetupThread; /** * Prepares notification and starts the service in the foreground. @@ -781,50 +785,47 @@ public class PlaybackService extends Service { PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT); - if (notificationSetupTask != null) { - notificationSetupTask.cancel(true); + if (notificationSetupThread != null) { + notificationSetupThread.interrupt(); } - notificationSetupTask = new AsyncTask<Void, Void, Void>() { + Runnable notificationSetupTask = new Runnable() { Bitmap icon = null; @Override - protected Void doInBackground(Void... params) { + public void run() { Log.d(TAG, "Starting background work"); if (android.os.Build.VERSION.SDK_INT >= 11) { if (info.playable != null) { + int iconSize = getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); try { - int iconSize = getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - icon = Picasso.with(PlaybackService.this) + icon = Glide.with(PlaybackService.this) .load(info.playable.getImageUri()) - .resize(iconSize, iconSize) + .asBitmap() + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .into(-1, -1) // this resizing would not be exact, so we have + // scale the bitmap ourselves .get(); - } catch (IOException e) { + icon = Bitmap.createScaledBitmap(icon, iconSize, iconSize, true); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { e.printStackTrace(); } } - } if (icon == null) { icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext())); } - return null; - } - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); if (mediaPlayer == null) { return; } - PlaybackServiceMediaPlayer.PSMPInfo newInfo = mediaPlayer.getPSMPInfo(); + PlayerStatus playerStatus = mediaPlayer.getPlayerStatus(); final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()); - if (!isCancelled() && - started && - info.playable != null) { + if (!Thread.currentThread().isInterrupted() && started && info.playable != null) { String contentText = info.playable.getFeedTitle(); String contentTitle = info.playable.getEpisodeTitle(); Notification notification = null; @@ -865,7 +866,7 @@ public class PlaybackService extends Service { .setLargeIcon(icon) .setSmallIcon(smallIcon) .setPriority(UserPreferences.getNotifyPriority()); // set notification priority - if (newInfo.playerStatus == PlayerStatus.PLAYING) { + if (playerStatus == PlayerStatus.PLAYING) { notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action getString(R.string.pause_label), pauseButtonPendingIntent); @@ -902,15 +903,9 @@ public class PlaybackService extends Service { Log.d(TAG, "Notification set up"); } } - }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - notificationSetupTask - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - notificationSetupTask.execute(); - } - + notificationSetupThread = new Thread(notificationSetupTask); + notificationSetupThread.start(); } /** @@ -923,7 +918,7 @@ public class PlaybackService extends Service { int position = getCurrentPosition(); int duration = getDuration(); float playbackSpeed = getCurrentPlaybackSpeed(); - final Playable playable = mediaPlayer.getPSMPInfo().playable; + final Playable playable = mediaPlayer.getPlayable(); if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) { Log.d(TAG, "Saving current position to " + position); if (updatePlayedDuration && playable instanceof FeedMedia) { @@ -1200,12 +1195,10 @@ public class PlaybackService extends Service { } public PlayerStatus getStatus() { - return mediaPlayer.getPSMPInfo().playerStatus; + return mediaPlayer.getPlayerStatus(); } - public Playable getPlayable() { - return mediaPlayer.getPSMPInfo().playable; - } + public Playable getPlayable() { return mediaPlayer.getPlayable(); } public void setSpeed(float speed) { mediaPlayer.setSpeed(speed); @@ -1231,7 +1224,7 @@ public class PlaybackService extends Service { public void seekTo(final int t) { if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING && GpodnetPreferences.loggedIn()) { - final Playable playable = mediaPlayer.getPSMPInfo().playable; + final Playable playable = mediaPlayer.getPlayable(); if (playable instanceof FeedMedia) { FeedMedia media = (FeedMedia) playable; FeedItem item = media.getItem(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java index 7a8e38c59..835a8c1d1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java @@ -196,7 +196,7 @@ public class PlaybackServiceMediaPlayer { DBWriter.markItemRead(context, item, true, false); DBWriter.removeQueueItem(context, item, false); DBWriter.addItemToPlaybackHistory(context, oldMedia); - if (UserPreferences.isAutoDelete()) { + if (item.getFeed().getPreferences().getCurrentAutoDelete()) { Log.d(TAG, "Delete " + oldMedia.toString()); DBWriter.deleteFeedMediaOfItem(context, oldMedia.getId()); } @@ -623,10 +623,6 @@ public class PlaybackServiceMediaPlayer { return mediaType; } - public PlayerStatus getPlayerStatus() { - return playerStatus; - } - public boolean isStreaming() { return stream; } @@ -706,6 +702,26 @@ public class PlaybackServiceMediaPlayer { } /** + * Returns the current status, if you need the media and the player status together, you should + * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition + * could result in nonsensical results (like a status of PLAYING, but a null playable) + * @return the current player status + */ + public PlayerStatus getPlayerStatus() { + return playerStatus; + } + + /** + * Returns the current media, if you need the media and the player status together, you should + * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition + * could result in nonsensical results (like a status of PLAYING, but a null playable) + * @return the current media. May be null + */ + public Playable getPlayable() { + return media; + } + + /** * Returns a token to this object's MediaSession. The MediaSession should only be used for notifications * at the moment. * diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java index f647fd537..a07705e69 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java @@ -28,7 +28,7 @@ public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> { List<FeedItem> delete; for (FeedItem item : downloadedItems) { if (item.hasMedia() && item.getMedia().isDownloaded() - && !queue.contains(item.getId()) && item.isRead()) { + && !queue.contains(item.getId()) && item.isPlayed()) { candidates.add(item); } 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 92de1eee7..c2e971dce 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 @@ -22,7 +22,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { private final APCleanupAlgorithm cleanupAlgorithm = new APCleanupAlgorithm(); /** - * Looks for undownloaded episodes in the queue or list of unread items and request a download if + * Looks for undownloaded episodes in the queue or list of new items and request a download if * 1. Network is available * 2. The device is charging or the user allows auto download on battery * 3. There is free space in the episode cache @@ -40,7 +40,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { public void run() { // true if we should auto download based on network status - boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context) + boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable() && UserPreferences.isEnableAutodownload(); // true if we should auto download based on power status @@ -57,12 +57,12 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm { candidates = DBReader.getFeedItems(context, mediaIds); } else { final List<FeedItem> queue = DBReader.getQueue(context); - final List<FeedItem> unreadItems = DBReader.getUnreadItemsList(context); - candidates = new ArrayList<FeedItem>(queue.size() + unreadItems.size()); + final List<FeedItem> newItems = DBReader.getNewItemsList(context); + candidates = new ArrayList<FeedItem>(queue.size() + newItems.size()); candidates.addAll(queue); - for(FeedItem unreadItem : unreadItems) { - if(candidates.contains(unreadItem) == false) { - candidates.add(unreadItem); + for(FeedItem newItem : newItems) { + if(candidates.contains(newItem) == false) { + candidates.add(newItem); } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java deleted file mode 100644 index 420bbc09d..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java +++ /dev/null @@ -1,139 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import android.content.Context; -import android.util.Log; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ExecutionException; - -import de.danoeh.antennapod.core.feed.FeedItem; - -/** - * Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPodSP apps. - */ -public class APSPCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> { - private static final String TAG = "APSPCleanupAlgorithm"; - - final int numberOfNewAutomaticallyDownloadedEpisodes; - - public APSPCleanupAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) { - this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes; - } - - /** - * Performs an automatic cleanup. Episodes that have been downloaded first will also be deleted first. - * The episode that is currently playing as well as the n most recent episodes (the exact value is determined - * by AppPreferences.numberOfNewAutomaticallyDownloadedEpisodes) will never be deleted. - * - * @param context - * @param episodeSize The maximum amount of space that should be freed by this method - * @return The number of episodes that have been deleted - */ - @Override - public int performCleanup(Context context, Integer episodeSize) { - Log.i(TAG, String.format("performAutoCleanup(%d)", episodeSize)); - if (episodeSize <= 0) { - return 0; - } - - List<FeedItem> candidates = getAutoCleanupCandidates(context); - List<FeedItem> deleteList = new ArrayList<FeedItem>(); - long deletedEpisodesSize = 0; - Collections.sort(candidates, new Comparator<FeedItem>() { - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - File lFile = new File(lhs.getMedia().getFile_url()); - File rFile = new File(rhs.getMedia().getFile_url()); - if (!lFile.exists() || !rFile.exists()) { - return 0; - } - if (FileUtils.isFileOlder(lFile, rFile)) { - return -1; - } else { - return 1; - } - } - }); - // listened episodes will be deleted first - Iterator<FeedItem> it = candidates.iterator(); - if (it.hasNext()) { - for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) { - if (!i.getMedia().isPlaying() && i.getMedia().getPlaybackCompletionDate() != null) { - it.remove(); - deleteList.add(i); - deletedEpisodesSize += i.getMedia().getSize(); - } - } - } - - // delete unlistened old episodes if necessary - it = candidates.iterator(); - if (it.hasNext()) { - for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) { - if (!i.getMedia().isPlaying()) { - it.remove(); - deleteList.add(i); - deletedEpisodesSize += i.getMedia().getSize(); - } - } - } - for (FeedItem item : deleteList) { - try { - DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); - } - } - Log.i(TAG, String.format("performAutoCleanup(%d) deleted %d episodes and freed %d bytes of memory", - episodeSize, deleteList.size(), deletedEpisodesSize)); - return deleteList.size(); - } - - @Override - public Integer getDefaultCleanupParameter(Context context) { - return 0; - } - - @Override - public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) { - int episodeSize = 0; - for (FeedItem item : items) { - if (item.hasMedia() && !item.getMedia().isDownloaded()) { - episodeSize += item.getMedia().getSize(); - } - } - return episodeSize; - } - - /** - * Returns list of FeedItems that have been downloaded, but are not one of the - * [numberOfNewAutomaticallyDownloadedEpisodes] most recent items. - */ - private List<FeedItem> getAutoCleanupCandidates(Context context) { - List<FeedItem> downloaded = new ArrayList<FeedItem>(DBReader.getDownloadedItems(context)); - List<FeedItem> recent = new ArrayList<FeedItem>(DBReader.getRecentlyPublishedEpisodes(context, - numberOfNewAutomaticallyDownloadedEpisodes)); - for (FeedItem r : recent) { - if (r.hasMedia() && r.getMedia().isDownloaded()) { - for (int i = 0; i < downloaded.size(); i++) { - if (downloaded.get(i).getId() == r.getId()) { - downloaded.remove(i); - break; - } - } - } - } - - return downloaded; - - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java deleted file mode 100644 index f760ec0ce..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.danoeh.antennapod.core.storage; - -import android.content.Context; -import android.util.Log; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.NetworkUtils; - -/** - * Implements the automatic download algorithm used by AntennaPodSP apps. - */ -public class APSPDownloadAlgorithm implements AutomaticDownloadAlgorithm { - private static final String TAG = "APSPDownloadAlgorithm"; - - private final int numberOfNewAutomaticallyDownloadedEpisodes; - - public APSPDownloadAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) { - this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes; - } - - /** - * Downloads the most recent episodes automatically. The exact number of - * episodes that will be downloaded can be set in the AppPreferences. - * - * @param context Used for accessing the DB. - * @return A Runnable that will be submitted to an ExecutorService. - */ - @Override - public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) { - return new Runnable() { - @Override - public void run() { - if (BuildConfig.DEBUG) - Log.d(TAG, "Performing auto-dl of undownloaded episodes"); - if (NetworkUtils.autodownloadNetworkAvailable(context) - && UserPreferences.isEnableAutodownload()) { - - Arrays.sort(mediaIds); - List<FeedItem> itemsToDownload = DBReader.getRecentlyPublishedEpisodes(context, - numberOfNewAutomaticallyDownloadedEpisodes); - Iterator<FeedItem> it = itemsToDownload.iterator(); - - for (FeedItem item = it.next(); it.hasNext(); item = it.next()) { - if (!item.hasMedia() - || item.getMedia().isDownloaded() - || Arrays.binarySearch(mediaIds, item.getMedia().getId()) < 0) { - it.remove(); - } - } - if (BuildConfig.DEBUG) - Log.d(TAG, "Enqueueing " + itemsToDownload.size() - + " items for automatic download"); - if (!itemsToDownload.isEmpty()) { - try { - DBTasks.downloadFeedItems(false, context, - itemsToDownload.toArray(new FeedItem[itemsToDownload - .size()])); - } catch (DownloadRequestException e) { - e.printStackTrace(); - } - } - } - } - }; - } -} 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 dc24c5784..5ed0a6ee3 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 @@ -22,6 +22,7 @@ import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.feed.ID3Chapter; import de.danoeh.antennapod.core.feed.SimpleChapter; import de.danoeh.antennapod.core.feed.VorbisCommentChapter; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.LongIntMap; @@ -114,36 +115,6 @@ public final class DBReader { } /** - * Returns a list of 'expired Feeds', i.e. Feeds that have not been updated for a certain amount of time. - * - * @param context A context that is used for opening a database connection. - * @param expirationTime Time that is used for determining whether a feed is outdated or not. - * A Feed is considered expired if 'lastUpdate < (currentTime - expirationTime)' evaluates to true. - * @return A list of Feeds, sorted alphabetically by their title. A Feed-object - * of the returned list does NOT have its list of FeedItems yet. The FeedItem-list - * can be loaded separately with {@link #getFeedItemList(android.content.Context, de.danoeh.antennapod.core.feed.Feed)}. - */ - public static List<Feed> getExpiredFeedsList(final Context context, final long expirationTime) { - if (BuildConfig.DEBUG) - Log.d(TAG, String.format("getExpiredFeedsList(%d)", expirationTime)); - - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - - Cursor feedlistCursor = adapter.getExpiredFeedsCursor(expirationTime); - List<Feed> feeds = new ArrayList<Feed>(feedlistCursor.getCount()); - - if (feedlistCursor.moveToFirst()) { - do { - Feed feed = extractFeedFromCursorRow(adapter, feedlistCursor); - feeds.add(feed); - } while (feedlistCursor.moveToNext()); - } - feedlistCursor.close(); - return feeds; - } - - /** * Takes a list of FeedItems and loads their corresponding Feed-objects from the database. * The feedID-attribute of a FeedItem must be set to the ID of its feed or the method will * not find the correct feed of an item. @@ -230,7 +201,7 @@ public final class DBReader { new FlattrStatus(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS)), itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0, image, - (itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0), + itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_READ), itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER), itemlistCursor.getInt(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD)) > 0 ); @@ -274,6 +245,18 @@ public final class DBReader { playbackCompletionDate = new Date( playbackCompletionTime); } + Boolean hasEmbeddedPicture; + switch(cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) { + case 1: + hasEmbeddedPicture = Boolean.TRUE; + break; + case 0: + hasEmbeddedPicture = Boolean.FALSE; + break; + default: + hasEmbeddedPicture = null; + break; + } return new FeedMedia( mediaId, @@ -286,7 +269,8 @@ public final class DBReader { cursor.getString(PodDBAdapter.KEY_DOWNLOAD_URL_INDEX), cursor.getInt(PodDBAdapter.KEY_DOWNLOADED_INDEX) > 0, playbackCompletionDate, - cursor.getInt(PodDBAdapter.KEY_PLAYED_DURATION_INDEX)); + cursor.getInt(PodDBAdapter.KEY_PLAYED_DURATION_INDEX), + hasEmbeddedPicture); } private static Feed extractFeedFromCursorRow(PodDBAdapter adapter, @@ -325,9 +309,9 @@ public final class DBReader { if (image != null) { image.setOwner(feed); } - FeedPreferences preferences = new FeedPreferences(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID), cursor.getInt(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_AUTO_DOWNLOAD) > 0, + FeedPreferences.AutoDeleteAction.values()[cursor.getInt(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_AUTO_DELETE_ACTION)], cursor.getString(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_USERNAME), cursor.getString(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_PASSWORD)); @@ -469,8 +453,7 @@ public final class DBReader { * Loads a list of FeedItems whose 'read'-attribute is set to false. * * @param context A context that is used for opening a database connection. - * @return A list of FeedItems whose 'read'-attribute it set to false. If the FeedItems in the list are not used, - * consider using {@link #getUnreadItemIds(android.content.Context)} instead. + * @return A list of FeedItems whose 'read'-attribute it set to false. */ public static List<FeedItem> getUnreadItemsList(Context context) { if (BuildConfig.DEBUG) @@ -538,6 +521,28 @@ public final class DBReader { return itemIds; } + /** + * Loads FeedMedia whose file size is unknown + * + * @param context A context that is used for opening a database connection. + * @return A list of FeedMedia items whose size is 0 (unknown and never tried to + * determine the correct size) + */ + public static List<FeedMedia> getFeedMediaUnknownSize(Context context) { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + Cursor cursor = adapter.getFeedMediaUnknownSizeCursor(); + List<FeedMedia> result = new ArrayList<>(cursor.getCount()); + if (cursor.moveToFirst()) { + do { + FeedMedia media = extractFeedMediaFromCursorRow(cursor); + result.add(media); + } while (cursor.moveToNext()); + } + cursor.close(); + return result; + } + /** * Loads a list of FeedItems sorted by pubDate in descending order. @@ -866,7 +871,11 @@ public final class DBReader { if (cursor.moveToFirst()) { String username = cursor.getString(0); String password = cursor.getString(1); - return username + ":" + password; + if(username != null && password != null) { + return username + ":" + password; + } else { + return ""; + } } return ""; } finally { @@ -1001,20 +1010,6 @@ public final class DBReader { } /** - * Returns a map containing the number of unread items per feed - * - * @param context A context that is used for opening a database connection. - * @return The number of unread items per feed. - */ - public static LongIntMap getNumberOfUnreadFeedItems(final Context context, long... feedIds) { - PodDBAdapter adapter = new PodDBAdapter(context); - adapter.open(); - final LongIntMap result = adapter.getNumberOfUnreadFeedItems(feedIds); - adapter.close(); - return result; - } - - /** * Searches the DB for a FeedImage of the given id. * * @param context A context that is used for opening a database connection. @@ -1142,27 +1137,42 @@ public final class DBReader { for(int i=0; i < feeds.size(); i++) { feedIds[i] = feeds.get(i).getId(); } - final LongIntMap numUnreadFeedItems = adapter.getNumberOfUnreadFeedItems(feedIds); - Collections.sort(feeds, new Comparator<Feed>() { - @Override - public int compare(Feed lhs, Feed rhs) { - long numUnreadLhs = numUnreadFeedItems.get(lhs.getId()); - Log.d(TAG, "feed with id " + lhs.getId() + " has " + numUnreadLhs + " unread items"); - long numUnreadRhs = numUnreadFeedItems.get(rhs.getId()); - Log.d(TAG, "feed with id " + rhs.getId() + " has " + numUnreadRhs + " unread items"); - if(numUnreadLhs > numUnreadRhs) { - // reverse natural order: podcast with most unplayed episodes first - return -1; - } else if(numUnreadLhs == numUnreadRhs) { + final LongIntMap feedCounters = adapter.getFeedCounters(feedIds); + + Comparator<Feed> comparator; + int feedOrder = UserPreferences.getFeedOrder(); + if(feedOrder == UserPreferences.FEED_ORDER_UNPLAYED_EPISODES) { + comparator = new Comparator<Feed>() { + @Override + public int compare(Feed lhs, Feed rhs) { + long counterLhs = feedCounters.get(lhs.getId()); + long counterRhs = feedCounters.get(rhs.getId()); + if(counterLhs > counterRhs) { + // reverse natural order: podcast with most unplayed episodes first + return -1; + } else if(counterLhs == counterRhs) { + return lhs.getTitle().compareTo(rhs.getTitle()); + } else { + return 1; + } + } + }; + } else { + comparator = new Comparator<Feed>() { + @Override + public int compare(Feed lhs, Feed rhs) { + if(lhs.getTitle() == null) { + return 1; + } return lhs.getTitle().compareTo(rhs.getTitle()); - } else { - return 1; } - } - }); + }; + } + + Collections.sort(feeds, comparator); int queueSize = adapter.getQueueSize(); int numNewItems = adapter.getNumberOfNewItems(); - NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numUnreadFeedItems); + NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, feedCounters); adapter.close(); return result; } @@ -1171,14 +1181,16 @@ public final class DBReader { public List<Feed> feeds; public int queueSize; public int numNewItems; - public LongIntMap numUnreadFeedItems; + public LongIntMap feedCounters; - public NavDrawerData(List<Feed> feeds, int queueSize, int numNewItems, - LongIntMap numUnreadFeedItems) { + public NavDrawerData(List<Feed> feeds, + int queueSize, + int numNewItems, + LongIntMap feedIndicatorValues) { this.feeds = feeds; this.queueSize = queueSize; this.numNewItems = numNewItems; - this.numUnreadFeedItems = numUnreadFeedItems; + this.feedCounters = feedIndicatorValues; } } } 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 defce5930..770c4f7d7 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 @@ -5,6 +5,9 @@ import android.content.Intent; import android.database.Cursor; import android.util.Log; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,6 +32,7 @@ import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.FeedMediaSizeService; import de.danoeh.antennapod.core.service.GpodnetSyncService; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.playback.PlaybackService; @@ -180,6 +184,7 @@ public final class DBTasks { if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) { GpodnetSyncService.sendSyncIntent(context); } + Log.d(TAG, "refreshAllFeeds autodownload"); autodownloadUndownloadedItems(context); } }.start(); @@ -188,47 +193,6 @@ public final class DBTasks { } } - /** - * Used by refreshExpiredFeeds to determine which feeds should be refreshed. - * This method will use the value specified in the UserPreferences as the - * expiration time. - * - * @param context Used for DB access. - * @return A list of expired feeds. An empty list will be returned if there - * are no expired feeds. - */ - public static List<Feed> getExpiredFeeds(final Context context) { - long millis = UserPreferences.getUpdateInterval(); - - if (millis > 0) { - - List<Feed> feedList = DBReader.getExpiredFeedsList(context, - millis); - if (feedList.size() > 0) { - refreshFeeds(context, feedList); - } - return feedList; - } else { - return new ArrayList<Feed>(); - } - } - - /** - * Refreshes expired Feeds in the list returned by the getExpiredFeedsList(Context, long) method in DBReader. - * The expiration date parameter is determined by the update interval specified in {@link UserPreferences}. - * - * @param context Used for DB access. - */ - public static void refreshExpiredFeeds(final Context context) { - Log.d(TAG, "Refreshing expired feeds"); - - new Thread() { - public void run() { - refreshFeeds(context, getExpiredFeeds(context)); - } - }.start(); - } - private static void refreshFeeds(final Context context, final List<Feed> feedList) { @@ -320,24 +284,6 @@ public final class DBTasks { } /** - * Notifies the database about a missing FeedImage file. This method will attempt to re-download the file. - * - * @param context Used for requesting the download. - * @param image The FeedImage object. - */ - public static void notifyInvalidImageFile(final Context context, - final FeedImage image) { - Log.i(TAG, - "The DB was notified about an invalid image download. It will now try to re-download the image file"); - try { - DownloadRequester.getInstance().downloadImage(context, image); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Log.w(TAG, "Failed to download invalid feed image"); - } - } - - /** * Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the * DB and send a FeedUpdateBroadcast. */ @@ -439,6 +385,7 @@ public final class DBTasks { * @return A Future that can be used for waiting for the methods completion. */ public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) { + Log.d(TAG, "autodownloadUndownloadedItems"); return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm() .autoDownloadUndownloadedItems(context, mediaIds)); @@ -564,7 +511,7 @@ public final class DBTasks { // all new feeds will have the most recent item marked as unplayed FeedItem mostRecent = newFeed.getMostRecentItem(); if (mostRecent != null) { - mostRecent.setRead(false); + mostRecent.setNew(); } newFeedsList.add(newFeed); @@ -575,16 +522,16 @@ public final class DBTasks { Collections.sort(newFeed.getItems(), new FeedItemPubdateComparator()); - final boolean markNewItemsAsUnread; + final boolean markNewItems; if (newFeed.getPageNr() == savedFeed.getPageNr()) { if (savedFeed.compareWithOther(newFeed)) { Log.d(TAG, "Feed has updated attribute values. Updating old feed's attributes"); savedFeed.updateFromOther(newFeed); } - markNewItemsAsUnread = true; + markNewItems = true; } else { Log.d(TAG, "New feed has a higher page number. Merging without marking as unread"); - markNewItemsAsUnread = false; + markNewItems = false; savedFeed.setNextPageLink(newFeed.getNextPageLink()); } if (savedFeed.getPreferences().compareWithOther(newFeed.getPreferences())) { @@ -598,12 +545,11 @@ public final class DBTasks { item.getIdentifyingValue()); if (oldItem == null) { // item is new - final int i = idx; item.setFeed(savedFeed); item.setAutoDownload(savedFeed.getPreferences().getAutoDownload()); - savedFeed.getItems().add(i, item); - if (markNewItemsAsUnread) { - item.setRead(false); + savedFeed.getItems().add(idx, item); + if (markNewItems) { + item.setNew(); } } else { oldItem.updateFromOther(item); @@ -632,6 +578,8 @@ public final class DBTasks { EventDistributor.getInstance().sendFeedUpdateBroadcast(); + context.startService(new Intent(context, FeedMediaSizeService.class)); + return resultFeeds; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index fe5d0dfd3..12bc208a6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -13,10 +13,10 @@ import org.shredzone.flattr4j.model.Flattr; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -100,6 +100,7 @@ public class DBWriter { } media.setDownloaded(false); media.setFile_url(null); + media.setHasEmbeddedPicture(false); PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); adapter.setMedia(media); @@ -184,13 +185,15 @@ public class DBWriter { } // delete stored media files and mark them as read List<FeedItem> queue = DBReader.getQueue(context); - boolean queueWasModified = false; + List<FeedItem> removed = new ArrayList<>(); if (feed.getItems() == null) { DBReader.getFeedItemList(context, feed); } for (FeedItem item : feed.getItems()) { - queueWasModified |= queue.remove(item); + if(queue.remove(item)) { + removed.add(item); + } if (item.getMedia() != null && item.getMedia().isDownloaded()) { File mediaFile = new File(item.getMedia() @@ -213,8 +216,10 @@ public class DBWriter { } PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); - if (queueWasModified) { + if (removed.size() > 0) { adapter.setQueue(queue); + EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.IRREVERSIBLE_REMOVED, + removed)); } adapter.removeFeed(feed); adapter.close(); @@ -351,7 +356,7 @@ public class DBWriter { final PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); final List<FeedItem> queue = DBReader.getQueue(context, adapter); - FeedItem item = null; + FeedItem item; if (queue != null) { if (!itemListContains(queue, itemId)) { @@ -360,6 +365,9 @@ public class DBWriter { queue.add(index, item); adapter.setQueue(queue); EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, index)); + if(item.isNew()) { + DBWriter.markItemRead(context, false, item.getId()); + } } } } @@ -374,14 +382,20 @@ public class DBWriter { } + public static Future<?> addQueueItem(final Context context, + final long... itemIds) { + return addQueueItem(context, false, itemIds); + } + /** * Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true. * If a FeedItem is already in the queue, the FeedItem will not change its position in the queue. * * @param context A context that is used for opening a database connection. + * @param performAutoDownload true if an auto-download process should be started after the operation. * @param itemIds IDs of the FeedItem objects that should be added to the queue. */ - public static Future<?> addQueueItem(final Context context, + public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload, final long... itemIds) { return dbExec.submit(new Runnable() { @@ -390,43 +404,45 @@ public class DBWriter { if (itemIds.length > 0) { final PodDBAdapter adapter = new PodDBAdapter(context); adapter.open(); - final List<FeedItem> queue = DBReader.getQueue(context, - adapter); + final List<FeedItem> queue = DBReader.getQueue(context, adapter); if (queue != null) { boolean queueModified = false; - boolean unreadItemsModified = false; - List<FeedItem> itemsToSave = new LinkedList<FeedItem>(); + LongList markAsUnplayedIds = new LongList(); for (int i = 0; i < itemIds.length; i++) { if (!itemListContains(queue, itemIds[i])) { - final FeedItem item = DBReader.getFeedItem( - context, itemIds[i]); + final FeedItem item = DBReader.getFeedItem(context, itemIds[i]); if (item != null) { // add item to either front ot back of queue boolean addToFront = UserPreferences.enqueueAtFront(); - - if(addToFront){ - queue.add(0, item); + if (addToFront) { + queue.add(0 + i, item); } else { queue.add(item); } - queueModified = true; + if(item.isNew()) { + markAsUnplayedIds.add(item.getId()); + } } } } if (queueModified) { adapter.setQueue(queue); EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue)); + if(markAsUnplayedIds.size() > 0) { + DBWriter.markItemRead(context, false, markAsUnplayedIds.toArray()); + } } } adapter.close(); - DBTasks.autodownloadUndownloadedItems(context); + if (performAutoDownload) { + DBTasks.autodownloadUndownloadedItems(context); + } } } }); - } /** @@ -594,16 +610,25 @@ public class DBWriter { adapter.close(); } - /** - * Sets the 'read'-attribute of a FeedItem to the specified value. + /* + * Sets the 'read'-attribute of all specified FeedItems * * @param context A context that is used for opening a database connection. - * @param itemId ID of the FeedItem * @param read New value of the 'read'-attribute + * @param itemIds IDs of the FeedItems. */ - public static Future<?> markItemRead(final Context context, final long itemId, - final boolean read) { - return markItemRead(context, itemId, read, 0, false); + public static Future<?> markItemRead(final Context context, final boolean read, final long... itemIds) { + return dbExec.submit(new Runnable() { + @Override + public void run() { + final PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + int played = read ? FeedItem.PLAYED : FeedItem.UNPLAYED; + adapter.setFeedItemRead(played, itemIds); + adapter.close(); + EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); + } + }); } @@ -645,6 +670,35 @@ public class DBWriter { * @param context A context that is used for opening a database connection. * @param feedId ID of the Feed. */ + public static Future<?> markFeedSeen(final Context context, final long feedId) { + return dbExec.submit(new Runnable() { + + @Override + public void run() { + final PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + Cursor itemCursor = adapter.getNewItemsIdsCursor(feedId); + long[] ids = new long[itemCursor.getCount()]; + itemCursor.moveToFirst(); + for (int i = 0; i < ids.length; i++) { + ids[i] = itemCursor.getLong(0); + itemCursor.moveToNext(); + } + itemCursor.close(); + adapter.setFeedItemRead(FeedItem.UNPLAYED, ids); + adapter.close(); + + EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); + } + }); + } + + /** + * Sets the 'read'-attribute of all FeedItems of a specific Feed to true. + * + * @param context A context that is used for opening a database connection. + * @param feedId ID of the Feed. + */ public static Future<?> markFeedRead(final Context context, final long feedId) { return dbExec.submit(new Runnable() { @@ -660,13 +714,12 @@ public class DBWriter { itemCursor.moveToNext(); } itemCursor.close(); - adapter.setFeedItemRead(true, itemIds); + adapter.setFeedItemRead(FeedItem.PLAYED, itemIds); adapter.close(); EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); } }); - } /** @@ -689,7 +742,7 @@ public class DBWriter { itemCursor.moveToNext(); } itemCursor.close(); - adapter.setFeedItemRead(true, itemIds); + adapter.setFeedItemRead(FeedItem.PLAYED, itemIds); adapter.close(); EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); @@ -1057,9 +1110,34 @@ public class DBWriter { EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); } }); + } + + /** + * Sets the 'auto_download'-attribute of specific FeedItem. + * + * @param context A context that is used for opening a database connection. + * @param feed This feed's episodes will be processed. + * @param autoDownload If true, auto download will be enabled for the feed's episodes. Else, + * it will be disabled. + */ + public static Future<?> setFeedsItemsAutoDownload(final Context context, final Feed feed, + final boolean autoDownload) { + Log.d(TAG, (autoDownload ? "Enabling" : "Disabling") + " auto download for items of feed " + feed.getId()); + return dbExec.submit(new Runnable() { + @Override + public void run() { + final PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + adapter.setFeedsItemsAutoDownload(feed, autoDownload); + adapter.close(); + + EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast(); + } + }); } + /** * Set filter of the feed * diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java index 386304530..bc34523cc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java @@ -18,7 +18,6 @@ import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedFile; -import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadRequest; @@ -186,15 +185,6 @@ public class DownloadRequester { downloadFeed(context, feed, false); } - public synchronized void downloadImage(Context context, FeedImage image) - throws DownloadRequestException { - if (feedFileValid(image)) { - FeedFile container = (image.getOwner() instanceof FeedFile) ? (FeedFile) image.getOwner() : null; - download(context, image, container, new File(getImagefilePath(context), - getImagefileName(image)), false, null, null, 0, false, null); - } - } - public synchronized void downloadMedia(Context context, FeedMedia feedmedia) throws DownloadRequestException { if (feedFileValid(feedmedia)) { @@ -332,20 +322,6 @@ public class DownloadRequester { return "feed-" + FileNameGenerator.generateFileName(filename); } - public synchronized String getImagefilePath(Context context) - throws DownloadRequestException { - return getExternalFilesDirOrThrowException(context, IMAGE_DOWNLOADPATH) - .toString() + "/"; - } - - public synchronized String getImagefileName(FeedImage image) { - String filename = image.getDownload_url(); - if (image.getOwner() != null && image.getOwner().getHumanReadableIdentifier() != null) { - filename = image.getOwner().getHumanReadableIdentifier(); - } - return "image-" + FileNameGenerator.generateFileName(filename); - } - public synchronized String getMediafilePath(Context context, FeedMedia media) throws DownloadRequestException { File externalStorage = getExternalFilesDirOrThrowException( 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 6fabf9005..edb7598ab 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 @@ -9,6 +9,7 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; +import android.media.MediaMetadataRetriever; import android.text.TextUtils; import android.util.Log; @@ -18,7 +19,8 @@ import java.util.Arrays; import java.util.List; import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedComponent; @@ -26,11 +28,11 @@ import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedPreferences; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.LongIntMap; import de.danoeh.antennapod.core.util.flattr.FlattrStatus; - -; +import de.greenrobot.event.EventBus; // TODO Remove media column from feeditem table @@ -148,6 +150,7 @@ public class PodDBAdapter { public static final String KEY_CHAPTER_TYPE = "type"; public static final String KEY_PLAYBACK_COMPLETION_DATE = "playback_completion_date"; public static final String KEY_AUTO_DOWNLOAD = "auto_download"; + public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action"; public static final String KEY_PLAYED_DURATION = "played_duration"; public static final String KEY_USERNAME = "username"; public static final String KEY_PASSWORD = "password"; @@ -155,6 +158,8 @@ public class PodDBAdapter { public static final String KEY_NEXT_PAGE_LINK = "next_page_link"; public static final String KEY_HIDE = "hide"; public static final String KEY_LAST_UPDATE_FAILED = "last_update_failed"; + public static final String KEY_HAS_EMBEDDED_PICTURE = "has_embedded_picture"; + // Table names public static final String TABLE_NAME_FEEDS = "Feeds"; @@ -183,7 +188,8 @@ public class PodDBAdapter { + KEY_IS_PAGED + " INTEGER DEFAULT 0," + KEY_NEXT_PAGE_LINK + " TEXT," + KEY_HIDE + " TEXT," - + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0)"; + + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0," + + KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0)"; public static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE " + TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE @@ -209,7 +215,7 @@ public class PodDBAdapter { + KEY_PLAYBACK_COMPLETION_DATE + " INTEGER," + KEY_FEEDITEM + " INTEGER," + KEY_PLAYED_DURATION + " INTEGER," - + KEY_AUTO_DOWNLOAD + " INTEGER)"; + + KEY_HAS_EMBEDDED_PICTURE + " INTEGER)"; public static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE " + TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE @@ -279,6 +285,7 @@ public class PodDBAdapter { TABLE_NAME_FEEDS + "." + KEY_PASSWORD, TABLE_NAME_FEEDS + "." + KEY_HIDE, TABLE_NAME_FEEDS + "." + KEY_LAST_UPDATE_FAILED, + TABLE_NAME_FEEDS + "." + KEY_AUTO_DELETE_ACTION, }; // column indices for FEED_SEL_STD @@ -302,6 +309,7 @@ public class PodDBAdapter { public static final int IDX_FEED_SEL_STD_NEXT_PAGE_LINK = 17; public static final int IDX_FEED_SEL_PREFERENCES_USERNAME = 18; public static final int IDX_FEED_SEL_PREFERENCES_PASSWORD = 19; + public static final int IDX_FEED_SEL_PREFERENCES_AUTO_DELETE_ACTION = 22; /** * Select all columns from the feeditems-table except description and @@ -364,8 +372,7 @@ public class PodDBAdapter { private static synchronized PodDBHelper getDbHelperSingleton(Context appContext) { if (dbHelperSingleton == null) { - dbHelperSingleton = new PodDBHelper(appContext, DATABASE_NAME, null, - ClientConfig.storageCallbacks.getDatabaseVersion()); + dbHelperSingleton = new PodDBHelper(appContext, DATABASE_NAME, null); } return dbHelperSingleton; } @@ -458,6 +465,7 @@ public class PodDBAdapter { } ContentValues values = new ContentValues(); values.put(KEY_AUTO_DOWNLOAD, prefs.getAutoDownload()); + values.put(KEY_AUTO_DELETE_ACTION,prefs.getAutoDeleteAction().ordinal()); values.put(KEY_USERNAME, prefs.getUsername()); values.put(KEY_PASSWORD, prefs.getPassword()); db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(prefs.getFeedID())}); @@ -516,6 +524,7 @@ public class PodDBAdapter { values.put(KEY_DOWNLOAD_URL, media.getDownload_url()); values.put(KEY_DOWNLOADED, media.isDownloaded()); values.put(KEY_FILE_URL, media.getFile_url()); + values.put(KEY_HAS_EMBEDDED_PICTURE, media.hasEmbeddedPicture()); if (media.getPlaybackCompletionDate() != null) { values.put(KEY_PLAYBACK_COMPLETION_DATE, media @@ -736,7 +745,13 @@ public class PodDBAdapter { setFeed(item.getFeed()); } values.put(KEY_FEED, item.getFeed().getId()); - values.put(KEY_READ, item.isRead()); + if(item.isNew()) { + values.put(KEY_READ, FeedItem.NEW); + } else if(item.isPlayed()) { + values.put(KEY_READ, FeedItem.PLAYED); + } else { + values.put(KEY_READ, FeedItem.UNPLAYED); + } values.put(KEY_HAS_CHAPTERS, item.getChapters() != null || item.hasChapters()); values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier()); values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong()); @@ -768,7 +783,7 @@ public class PodDBAdapter { db.beginTransaction(); ContentValues values = new ContentValues(); - values.put(KEY_READ, read); + values.put(KEY_READ, read ? FeedItem.PLAYED : FeedItem.UNPLAYED); db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?", new String[]{String.valueOf(itemId)}); if (resetMediaPosition) { @@ -781,7 +796,7 @@ public class PodDBAdapter { db.endTransaction(); } - public void setFeedItemRead(boolean read, long... itemIds) { + public void setFeedItemRead(int read, long... itemIds) { db.beginTransaction(); ContentValues values = new ContentValues(); for (long id : itemIds) { @@ -846,6 +861,13 @@ public class PodDBAdapter { new String[]{String.valueOf(feedItem.getId())}); } + public void setFeedsItemsAutoDownload(Feed feed, boolean autoDownload) { + final String sql = "UPDATE " + TABLE_NAME_FEED_ITEMS + + " SET " + KEY_AUTO_DOWNLOAD + "="+ (autoDownload ? "1" : "0") + + " WHERE " + KEY_FEED + "=" + feed.getId(); + db.execSQL(sql); + } + public long getDownloadLogSize() { final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_DOWNLOAD_LOG); Cursor result = db.rawQuery(query, null); @@ -874,8 +896,7 @@ public class PodDBAdapter { values.put(KEY_ID, i); values.put(KEY_FEEDITEM, item.getId()); values.put(KEY_FEED, item.getFeed().getId()); - db.insertWithOnConflict(TABLE_NAME_QUEUE, null, values, - SQLiteDatabase.CONFLICT_REPLACE); + db.insertWithOnConflict(TABLE_NAME_QUEUE, null, values, SQLiteDatabase.CONFLICT_REPLACE); } db.setTransactionSuccessful(); db.endTransaction(); @@ -967,13 +988,6 @@ public class PodDBAdapter { return db.query(TABLE_NAME_FEEDS, new String[]{KEY_ID, KEY_DOWNLOAD_URL}, null, null, null, null, null); } - public final Cursor getExpiredFeedsCursor(long expirationTime) { - Cursor c = db.query(TABLE_NAME_FEEDS, FEED_SEL_STD, KEY_LASTUPDATE + " < " + String.valueOf(System.currentTimeMillis() - expirationTime), - null, null, null, - null); - return c; - } - /** * Returns a cursor with all FeedItems of a Feed. Uses FEEDITEM_SEL_FI_SMALL * @@ -1053,6 +1067,7 @@ public class PodDBAdapter { /** * Returns a cursor which contains all feed items in the queue. The returned * cursor uses the FEEDITEM_SEL_FI_SMALL selection. + * cursor uses the FEEDITEM_SEL_FI_SMALL selection. */ public final Cursor getQueueCursor() { Object[] args = (Object[]) new String[]{ @@ -1084,44 +1099,46 @@ public class PodDBAdapter { */ public final Cursor getUnreadItemsCursor() { Cursor c = db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_READ - + "=0", null, null, null, KEY_PUBDATE + " DESC"); + + "<" + FeedItem.PLAYED, null, null, null, KEY_PUBDATE + " DESC"); return c; } public final Cursor getNewItemIdsCursor() { - final String query = "SELECT " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + final String query = "SELECT " + KEY_ID + " FROM " + TABLE_NAME_FEED_ITEMS - + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM - + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM - + " WHERE " - + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed - + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded - + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played - + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue + + " WHERE " + KEY_READ + "=" + FeedItem.NEW; + return db.rawQuery(query, null); + } + + public final Cursor getFeedMediaUnknownSizeCursor() { + final String query = "SELECT * " + + " FROM " + TABLE_NAME_FEED_MEDIA + + " WHERE " + KEY_SIZE + "<= 0"; return db.rawQuery(query, null); } /** + * Returns a cursor which contains all items of a feed that are considered new. + * The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection. + */ + public final Cursor getNewItemsIdsCursor(long feedId) { + final String query = "SELECT " + KEY_ID + + " FROM " + TABLE_NAME_FEED_ITEMS + + " WHERE " + KEY_FEED + "=" + feedId + + " AND " + KEY_READ + "=" + FeedItem.NEW + + " ORDER BY " + KEY_PUBDATE + " DESC"; + Cursor c = db.rawQuery(query, null); + return c; + } + + /** * Returns a cursor which contains all feed items that are considered new. * The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection. */ public final Cursor getNewItemsCursor() { - final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS - + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM - + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM - + " WHERE " - + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed - + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded - + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played - + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL" // not in queue + final String query = "SELECT " + SEL_FI_SMALL_STR + + " FROM " + TABLE_NAME_FEED_ITEMS + + " WHERE " + KEY_READ + "=" + FeedItem.NEW + " ORDER BY " + KEY_PUBDATE + " DESC"; Cursor c = db.rawQuery(query, null); return c; @@ -1133,11 +1150,11 @@ public class PodDBAdapter { } public Cursor getDownloadedItemsCursor() { - final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS - + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + " WHERE " - + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0"; + final String query = "SELECT " + SEL_FI_SMALL_STR + + " FROM " + TABLE_NAME_FEED_ITEMS + + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + + " WHERE " + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0"; Cursor c = db.rawQuery(query, null); return c; } @@ -1271,19 +1288,9 @@ public class PodDBAdapter { } public final int getNumberOfNewItems() { - final String query = "SELECT COUNT(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ")" - +" FROM " + TABLE_NAME_FEED_ITEMS - + " LEFT JOIN " + TABLE_NAME_FEED_MEDIA + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM - + " LEFT JOIN " + TABLE_NAME_QUEUE + " ON " - + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" - + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM - + " WHERE " - + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed - + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded - + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played - + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue + final String query = "SELECT COUNT(" + KEY_ID + ")" + + " FROM " + TABLE_NAME_FEED_ITEMS + + " WHERE " + KEY_READ + "=" + FeedItem.NEW; Cursor c = db.rawQuery(query, null); int result = 0; if (c.moveToFirst()) { @@ -1293,7 +1300,20 @@ public class PodDBAdapter { return result; } - public final LongIntMap getNumberOfUnreadFeedItems(long... feedIds) { + public final LongIntMap getFeedCounters(long... feedIds) { + int counter = UserPreferences.getFeedCounter(); + String whereRead; + if(counter == UserPreferences.FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM) { + whereRead = "(" + KEY_READ + "=" + FeedItem.NEW + + " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")"; + } else if(counter == UserPreferences.FEED_COUNTER_SHOW_NEW) { + whereRead = KEY_READ + "=" + FeedItem.NEW; + } else if(counter == UserPreferences.FEED_COUNTER_SHOW_UNPLAYED) { + whereRead = KEY_READ + "=" + FeedItem.UNPLAYED; + } else { + return new LongIntMap(0); + } + // work around TextUtils.join wanting only boxed items // and StringUtils.join() causing NoSuchMethodErrors on MIUI StringBuilder builder = new StringBuilder(); @@ -1309,8 +1329,8 @@ public class PodDBAdapter { final String query = "SELECT " + KEY_FEED + ", COUNT(" + KEY_ID + ") AS count " + " FROM " + TABLE_NAME_FEED_ITEMS + " WHERE " + KEY_FEED + " IN (" + builder.toString() + ") " - + " AND " + KEY_READ + " = 0" - + " GROUP BY " + KEY_FEED; + + " AND " + whereRead + " GROUP BY " + KEY_FEED; + Cursor c = db.rawQuery(query, null); LongIntMap result = new LongIntMap(c.getCount()); if (c.moveToFirst()) { @@ -1463,17 +1483,22 @@ public class PodDBAdapter { * Helper class for opening the Antennapod database. */ private static class PodDBHelper extends SQLiteOpenHelper { + + private final static int VERSION = 1030002; + + private Context context; + /** * Constructor. * * @param context Context to use * @param name Name of the database * @param factory to use for creating cursor objects - * @param version number of the database */ public PodDBHelper(final Context context, final String name, - final CursorFactory factory, final int version) { - super(context, name, factory, version); + final CursorFactory factory) { + super(context, name, factory, VERSION); + this.context = context; } @Override @@ -1497,7 +1522,199 @@ public class PodDBAdapter { @Override public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { - ClientConfig.storageCallbacks.onUpgrade(db, oldVersion, newVersion); + EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database))); + Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to " + + newVersion + "."); + if (oldVersion <= 1) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " + + KEY_TYPE + " TEXT"); + } + if (oldVersion <= 2) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS + + " ADD COLUMN " + KEY_LINK + " TEXT"); + } + if (oldVersion <= 3) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " ADD COLUMN " + KEY_ITEM_IDENTIFIER + " TEXT"); + } + if (oldVersion <= 4) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN " + + KEY_FEED_IDENTIFIER + " TEXT"); + } + if (oldVersion <= 5) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG + + " ADD COLUMN " + KEY_REASON_DETAILED + " TEXT"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG + + " ADD COLUMN " + KEY_DOWNLOADSTATUS_TITLE + " TEXT"); + } + if (oldVersion <= 6) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS + + " ADD COLUMN " + KEY_CHAPTER_TYPE + " INTEGER"); + } + if (oldVersion <= 7) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " ADD COLUMN " + KEY_PLAYBACK_COMPLETION_DATE + + " INTEGER"); + } + if (oldVersion <= 8) { + final int KEY_ID_POSITION = 0; + final int KEY_MEDIA_POSITION = 1; + + // Add feeditem column to feedmedia table + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " ADD COLUMN " + KEY_FEEDITEM + + " INTEGER"); + Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS, + new String[]{KEY_ID, KEY_MEDIA}, "? > 0", + new String[]{KEY_MEDIA}, null, null, null); + if (feeditemCursor.moveToFirst()) { + db.beginTransaction(); + ContentValues contentValues = new ContentValues(); + do { + long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION); + contentValues.put(KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION)); + db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, KEY_ID + "=?", new String[]{String.valueOf(mediaId)}); + contentValues.clear(); + } while (feeditemCursor.moveToNext()); + db.setTransactionSuccessful(); + db.endTransaction(); + } + feeditemCursor.close(); + } + if (oldVersion <= 9) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_AUTO_DOWNLOAD + + " INTEGER DEFAULT 1"); + } + if (oldVersion <= 10) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_FLATTR_STATUS + + " INTEGER"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " ADD COLUMN " + KEY_FLATTR_STATUS + + " INTEGER"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " ADD COLUMN " + KEY_PLAYED_DURATION + + " INTEGER"); + } + if (oldVersion <= 11) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_USERNAME + + " TEXT"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_PASSWORD + + " TEXT"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " ADD COLUMN " + KEY_IMAGE + + " INTEGER"); + } + if (oldVersion <= 12) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_IS_PAGED + " INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_NEXT_PAGE_LINK + " TEXT"); + } + if (oldVersion <= 13) { + // remove duplicate rows in "Chapters" table that were created because of a bug. + db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " + + "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)", + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS, + KEY_ID, + KEY_ID, + KEY_ID, + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS, + KEY_TITLE, + KEY_START, + KEY_FEEDITEM, + KEY_LINK, + KEY_CHAPTER_TYPE)); + } + if(oldVersion <= 14) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " ADD COLUMN " + KEY_AUTO_DOWNLOAD + " INTEGER"); + db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " SET " + KEY_AUTO_DOWNLOAD + " = " + + "(SELECT " + KEY_AUTO_DOWNLOAD + + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS + + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + KEY_ID + + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + ")"); + + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_HIDE + " TEXT"); + + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0"); + + // create indexes + db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED); + db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE); + db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM); + db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM); + db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM); + } + if(oldVersion <= 15) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " ADD COLUMN " + KEY_HAS_EMBEDDED_PICTURE + " INTEGER DEFAULT -1"); + db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0" + + " WHERE " + KEY_DOWNLOADED + "=0"); + Cursor c = db.rawQuery("SELECT " + KEY_FILE_URL + + " FROM " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " WHERE " + KEY_DOWNLOADED + "=1 " + + " AND " + KEY_HAS_EMBEDDED_PICTURE + "=-1", null); + if(c.moveToFirst()) { + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + do { + String fileUrl = c.getString(0); + try { + mmr.setDataSource(fileUrl); + byte[] image = mmr.getEmbeddedPicture(); + if (image != null) { + db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=1" + + " WHERE " + KEY_FILE_URL + "='"+ fileUrl + "'"); + } else { + db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0" + + " WHERE " + KEY_FILE_URL + "='"+ fileUrl + "'"); + } + } catch(Exception e) { + e.printStackTrace(); + } + } while(c.moveToNext()); + } + c.close(); + } + if(oldVersion <= 16) { + String selectNew = "SELECT " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + + " FROM " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " INNER JOIN " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + " ON " + + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + + " LEFT OUTER JOIN " + PodDBAdapter.TABLE_NAME_QUEUE + " ON " + + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_FEEDITEM + + " WHERE " + + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed + + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded + + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played + + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue + String sql = "UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + + " SET " + KEY_READ + "=" + FeedItem.NEW + + " WHERE " + KEY_ID + " IN (" + selectNew + ")"; + Log.d("Migration", "SQL: " + sql); + db.execSQL(sql); + } + if(oldVersion <= 17) { + db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0"); + } + if(oldVersion < 1030005) { + db.execSQL("UPDATE FeedItems SET auto_download=0 WHERE " + + "(read=1 OR id IN (SELECT feeditem FROM FeedMedia WHERE position>0 OR downloaded=1)) " + + "AND id NOT IN (SELECT feeditem FROM Queue)"); + } + EventBus.getDefault().post(ProgressEvent.end()); } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java index 6455332be..31eb2efd6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java @@ -57,6 +57,10 @@ public class NSRSS20 extends Namespace { long size = 0; try { size = Long.parseLong(attributes.getValue(ENC_LEN)); + if(size < 16384) { + // less than 16kb is suspicious, check manually + size = 0; + } } catch (NumberFormatException e) { if (BuildConfig.DEBUG) Log.d(TAG, "Length attribute could not be parsed."); diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java index a0b514bd6..1b929b214 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java @@ -1,7 +1,10 @@ package de.danoeh.antennapod.core.util; +import android.content.Context; import android.util.Log; +import de.danoeh.antennapod.core.R; + /** Provides methods for converting various units. */ public final class Converter { /** Class shall not be instantiated. */ @@ -23,7 +26,7 @@ public final class Converter { /** Determines the length of the number for best readability.*/ private static final int NUM_LENGTH = 1024; - + private static final int DAYS_MIL = 86400000; private static final int HOURS_MIL = 3600000; private static final int MINUTES_MIL = 60000; private static final int SECONDS_MIL = 1000; @@ -99,5 +102,21 @@ public final class Converter { return Integer.valueOf(parts[0]) * 3600 * 1000 + Integer.valueOf(parts[1]) * 1000 * 60; } + + /** Converts milliseconds to a localized string containing hours and minutes */ + public static String getDurationStringLocalized(Context context, long duration) { + int h = (int)(duration / HOURS_MIL); + int rest = (int)(duration - h * HOURS_MIL); + int m = rest / MINUTES_MIL; + + String result = ""; + if(h > 0) { + String hours = context.getResources().getQuantityString(R.plurals.time_hours_quantified, h, h); + result += hours + " "; + } + String minutes = context.getResources().getQuantityString(R.plurals.time_minutes_quantified, m, m); + result += minutes; + return result; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java index b6df2dc85..c0233f684 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java @@ -8,6 +8,7 @@ import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; +import java.util.TimeZone; /** * Parses several date formats. @@ -16,49 +17,73 @@ public class DateUtils { private static final String TAG = "DateUtils"; + private static final SimpleDateFormat parser = new SimpleDateFormat("", Locale.US); + private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT"); + + static { + parser.setLenient(false); + parser.setTimeZone(defaultTimezone); + } + public static Date parse(final String input) { if(input == null) { - throw new IllegalArgumentException("Date most not be null"); + throw new IllegalArgumentException("Date must not be null"); } - String date = input.replace('/', '-'); + String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " "); + + // if datetime is more precise than seconds, make sure the value is in ms if(date.contains(".")) { int start = date.indexOf('.'); int current = start+1; while(current < date.length() && Character.isDigit(date.charAt(current))) { current++; } + // even more precise than microseconds: discard further decimal places if(current - start > 4) { if(current < date.length()-1) { date = date.substring(0, start + 4) + date.substring(current); } else { date = date.substring(0, start + 4); } + // less than 4 decimal places: pad to have a consistent format for the parser } else if(current - start < 4) { if(current < date.length()-1) { date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current); } else { date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)); } - } } String[] patterns = { "dd MMM yy HH:mm:ss Z", "dd MMM yy HH:mm Z", "EEE, dd MMM yyyy HH:mm:ss Z", + "EEE, dd MMM yyyy HH:mm:ss", "EEE, dd MMMM yyyy HH:mm:ss Z", + "EEE, dd MMMM yyyy HH:mm:ss", + "EEEE, dd MMM yyyy HH:mm:ss Z", "EEEE, dd MMM yy HH:mm:ss Z", + "EEEE, dd MMM yyyy HH:mm:ss", + "EEEE, dd MMM yy HH:mm:ss", "EEE MMM d HH:mm:ss yyyy", + "EEE, dd MMM yyyy HH:mm Z", + "EEE, dd MMM yyyy HH:mm", + "EEE, dd MMMM yyyy HH:mm Z", + "EEE, dd MMMM yyyy HH:mm", + "EEEE, dd MMM yyyy HH:mm Z", + "EEEE, dd MMM yy HH:mm Z", + "EEEE, dd MMM yyyy HH:mm", + "EEEE, dd MMM yy HH:mm", + "EEE MMM d HH:mm yyyy", "yyyy-MM-dd'T'HH:mm:ss", - "yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'HH:mm:ss.SSS Z", + "yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-ddZ", "yyyy-MM-dd" }; - SimpleDateFormat parser = new SimpleDateFormat("", Locale.US); - parser.setLenient(false); + ParsePosition pos = new ParsePosition(0); for(String pattern : patterns) { parser.applyPattern(pattern); @@ -69,7 +94,7 @@ public class DateUtils { } } - Log.d(TAG, "Could not parse date string \"" + input + "\""); + Log.d(TAG, "Could not parse date string \"" + input + "\" [" + date + "]"); return null; } @@ -109,6 +134,7 @@ public class DateUtils { public static String formatRFC3339UTC(Date date) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + format.setTimeZone(defaultTimezone); return format.format(date); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java new file mode 100644 index 000000000..2d5a6e5a1 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java @@ -0,0 +1,18 @@ +package de.danoeh.antennapod.core.util; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; + +import java.util.List; + +public class IntentUtils { + + public static boolean isCallable(final Context context, final Intent intent) { + List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); + return list.size() > 0; + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java index 3a349e221..9296039f0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java @@ -10,14 +10,16 @@ import android.util.Log; import java.util.Arrays; import java.util.List; -import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.preferences.UserPreferences; public class NetworkUtils { - private static final String TAG = "NetworkUtils"; - private NetworkUtils() { + private static final String TAG = NetworkUtils.class.getSimpleName(); + private static Context context; + + public static void init(Context context) { + NetworkUtils.context = context; } /** @@ -26,18 +28,16 @@ public class NetworkUtils { * network that is on the 'selected networks' list of the Wi-Fi filter for * automatic downloads and false otherwise. * */ - public static boolean autodownloadNetworkAvailable(Context context) { + public static boolean autodownloadNetworkAvailable() { ConnectivityManager cm = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); if (networkInfo != null) { if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Device is connected to Wi-Fi"); + Log.d(TAG, "Device is connected to Wi-Fi"); if (networkInfo.isConnected()) { if (!UserPreferences.isEnableAutodownloadWifiFilter()) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Auto-dl filter is disabled"); + Log.d(TAG, "Auto-dl filter is disabled"); return true; } else { WifiManager wm = (WifiManager) context @@ -48,31 +48,28 @@ public class NetworkUtils { .getAutodownloadSelectedNetworks()); if (selectedNetworks.contains(Integer.toString(wifiInfo .getNetworkId()))) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "Current network is on the selected networks list"); + Log.d(TAG, "Current network is on the selected networks list"); return true; } } } } } - if (BuildConfig.DEBUG) - Log.d(TAG, "Network for auto-dl is not available"); + Log.d(TAG, "Network for auto-dl is not available"); return false; } - public static boolean networkAvailable(Context context) { + public static boolean networkAvailable() { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); return info != null && info.isConnected(); } - public static boolean isDownloadAllowed(Context context) { - return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi(context); + public static boolean isDownloadAllowed() { + return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi(); } - public static boolean connectedToWifi(Context context) { + public static boolean connectedToWifi() { ConnectivityManager connManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connManager diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java index 85f32ed50..35916a604 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java @@ -2,6 +2,8 @@ package de.danoeh.antennapod.core.util; import android.content.Context; import android.content.Intent; + +import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; @@ -11,24 +13,49 @@ public class ShareUtils { private ShareUtils() {} - public static void shareLink(Context context, String link) { + public static void shareLink(Context context, String text) { Intent i = new Intent(Intent.ACTION_SEND); i.setType("text/plain"); - i.putExtra(Intent.EXTRA_SUBJECT, "Sharing URL"); - i.putExtra(Intent.EXTRA_TEXT, link); - context.startActivity(Intent.createChooser(i, "Share URL")); + i.putExtra(Intent.EXTRA_TEXT, text); + context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label))); } - - public static void shareFeedItemLink(Context context, FeedItem item) { - shareLink(context, item.getLink()); + + public static void shareFeedlink(Context context, Feed feed) { + shareLink(context, feed.getTitle() + ": " + feed.getLink()); } public static void shareFeedDownloadLink(Context context, Feed feed) { - shareLink(context, feed.getDownload_url()); + shareLink(context, feed.getTitle() + ": " + feed.getDownload_url()); } - - public static void shareFeedlink(Context context, Feed feed) { - shareLink(context, feed.getLink()); + + public static void shareFeedItemLink(Context context, FeedItem item) { + shareFeedItemLink(context, item, false); + } + + public static void shareFeedItemDownloadLink(Context context, FeedItem item) { + shareFeedItemDownloadLink(context, item, false); + } + + private static String getItemShareText(FeedItem item) { + return item.getFeed().getTitle() + ": " + item.getTitle(); + } + + public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) { + String text = getItemShareText(item) + " " + item.getLink(); + if(withPosition) { + int pos = item.getMedia().getPosition(); + text = item.getLink() + " [" + Converter.getDurationStringLong(pos) + "]"; + } + shareLink(context, text); + } + + public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) { + String text = getItemShareText(item) + " " + item.getMedia().getDownload_url(); + if(withPosition) { + int pos = item.getMedia().getPosition(); + text += " [" + Converter.getDurationStringLong(pos) + "]"; + } + shareLink(context, text); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java index 50792ae26..3b9e6120c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java @@ -42,12 +42,6 @@ public class FlattrUtils { private static final String PREF_ACCESS_TOKEN = "de.danoeh.antennapod.preference.flattrAccessToken"; - // Flattr URL for this app. - public static final String APP_URL = "http://antennapod.com"; - // Human-readable flattr-page. - public static final String APP_LINK = "https://flattr.com/thing/745609/"; - public static final String APP_THING_ID = "745609"; - private static volatile AccessToken cachedToken; private static AndroidAuthenticator createAuthenticator() { @@ -110,18 +104,6 @@ public class FlattrUtils { storeToken(null); } - public static Thing getAppThing(Context context) { - FlattrService fs = FlattrServiceCreator.getService(retrieveToken()); - try { - Thing thing = fs.getThing(Thing.withId(APP_THING_ID)); - return thing; - } catch (FlattrException e) { - e.printStackTrace(); - showErrorDialog(context, e.getMessage()); - return null; - } - } - public static void clickUrl(Context context, String url) throws FlattrException { if (hasToken()) { @@ -245,37 +227,6 @@ public class FlattrUtils { } } - public static void showForbiddenDialog(final Context context, - final String url) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.action_forbidden_title); - builder.setMessage(R.string.action_forbidden_msg); - builder.setPositiveButton(R.string.authenticate_now_label, - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - context.startActivity( - ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context)); - } - - } - ); - builder.setNegativeButton(R.string.visit_website_label, - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - Uri uri = Uri.parse(url); - context.startActivity(new Intent(Intent.ACTION_VIEW, - uri)); - } - - } - ); - builder.create().show(); - } - public static void showErrorDialog(final Context context, final String msg) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.error_label); diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java index 7ebd580f7..752e95985 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java @@ -7,7 +7,7 @@ import android.util.Log; import java.util.List; -import de.danoeh.antennapod.core.asynctask.PicassoImageResource; +import de.danoeh.antennapod.core.asynctask.ImageResource; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; @@ -18,7 +18,7 @@ import de.danoeh.antennapod.core.util.ShownotesProvider; * Interface for objects that can be played by the PlaybackService. */ public interface Playable extends Parcelable, - ShownotesProvider, PicassoImageResource { + ShownotesProvider, ImageResource { /** * Save information about the playable in a preference so that it can be diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java index a0d12d3e7..ba5428b81 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java @@ -555,7 +555,7 @@ public abstract class PlaybackController { * Should be used by classes which implement the OnSeekBarChanged interface. */ public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) { - if (playbackService != null) { + if (playbackService != null && media != null) { playbackService.seekTo((int) (prog * media.getDuration())); setupPositionObserver(); } diff --git a/core/src/main/res/values-az/strings.xml b/core/src/main/res/values-az/strings.xml index b52ecf4a4..3610f837e 100644 --- a/core/src/main/res/values-az/strings.xml +++ b/core/src/main/res/values-az/strings.xml @@ -46,7 +46,6 @@ <string name="mark_all_read_label">Hamısını oxunmuş kimi işarələ</string> <string name="show_info_label">Məlumatı göstər</string> <string name="share_link_label">Web-səhifəyi paylaş</string> - <string name="share_source_label">Kanalı paylaş</string> <string name="feed_delete_confirmation_msg">Bütün kanallar və epizodlar silinəçək.</string> <!--actions on feeditems--> <string name="download_label">Yüklə</string> @@ -134,8 +133,6 @@ <string name="pref_followQueue_sum">Oynatma başa çatanda növbədə irəlidəki epizodu oynat</string> <string name="playback_pref">Oynatma</string> <string name="network_pref">Şəbəkə</string> - <string name="pref_autoUpdateIntervall_title">Təzələmə intervalı</string> - <string name="pref_autoUpdateIntervall_sum">Kanalın avtomatik təzələməsinin intervalını seç ya da keçir onu</string> <string name="pref_downloadMediaOnWifiOnly_sum">Təkçə Wi-Fi vasitəsiilə yüklə</string> <string name="pref_followQueue_title">Fasiləsiz oynatma</string> <string name="pref_downloadMediaOnWifiOnly_title">Wi-Fi vasitəsiilə yükləmə</string> @@ -210,5 +207,6 @@ <string name="downloading_label">Yükləmə...</string> <!--Content descriptions for image buttons--> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> </resources> diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml index be7a73e6d..c4887e512 100644 --- a/core/src/main/res/values-ca/strings.xml +++ b/core/src/main/res/values-ca/strings.xml @@ -71,7 +71,6 @@ <string name="show_info_label">Mostra informació</string> <string name="remove_feed_label">Esborra podcast</string> <string name="share_link_label">Comparteix l\'enllaç de la plana</string> - <string name="share_source_label">Comparteix l\'enllaç del canal</string> <string name="feed_delete_confirmation_msg">Confirmeu que, efectivament, voleu suprimir aquest canal i tots els episodis que us n\'heu baixat.</string> <string name="feed_remover_msg">S\'està esborrant el canal</string> <string name="load_complete_feed">S\'ha actualitzat el canal</string> @@ -197,8 +196,6 @@ <string name="pref_auto_delete_title">Esborrat automàtic</string> <string name="playback_pref">Reproducció</string> <string name="network_pref">Xarxa</string> - <string name="pref_autoUpdateIntervall_title">Interval d\'actualització</string> - <string name="pref_autoUpdateIntervall_sum">Especifiqueu l\'interval en què els canals s\'actualitzen de forma automàtica, o deshabiliteu la funcionalitat.</string> <string name="pref_downloadMediaOnWifiOnly_sum">Només baixa fitxers a través d\'una xarxa sense fils</string> <string name="pref_followQueue_title">Reproducció continuada</string> <string name="pref_downloadMediaOnWifiOnly_title">Baixa a través de xarxes sense fils</string> @@ -240,8 +237,6 @@ <string name="pref_gpodnet_setlogin_information_sum">Canvia les dades d\'inici de sessió del vostre compte de gpodder.net</string> <string name="pref_playback_speed_title">Velocitats de reproducció</string> <string name="pref_playback_speed_sum">Personalitzeu les velocitats disponibles per a una velocitat de reproducció d\'àudio variable</string> - <string name="pref_seek_delta_title">Salta a l\'instant</string> - <string name="pref_seek_delta_sum">Salta aquesta quantitat de segons en rebobinar o en avançar ràpidament</string> <string name="pref_gpodnet_sethostname_title">Definex nom del servidor</string> <string name="pref_gpodnet_sethostname_use_default_host">Utilitza el servidor per defecte</string> <string name="pref_expandNotify_title">Amplia la notificació</string> @@ -283,9 +278,6 @@ <string name="sleep_timer_label">Temporitzador</string> <string name="time_left_label">Temps restant:\u0020</string> <string name="time_dialog_invalid_input">L\'entrada no és vàlida, ja que el temps ha de ser un nombre i no ho és</string> - <string name="time_unit_seconds">segons</string> - <string name="time_unit_minutes">minuts</string> - <string name="time_unit_hours">hores</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">CATEGORIES</string> <string name="gpodnet_toplist_header">TOP PODCASTS</string> @@ -354,6 +346,7 @@ <!--Feed information screen--> <string name="authentication_label">Autenticació</string> <string name="authentication_descr">Canvieu el nom d\'usuari i contrasenya per a aquest podcast i els seus episodis.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">S\'estan important les subscripcions des de les apps de propòsit únic...</string> </resources> diff --git a/core/src/main/res/values-cs-rCZ/strings.xml b/core/src/main/res/values-cs-rCZ/strings.xml index 90304a404..4a89a7d8c 100644 --- a/core/src/main/res/values-cs-rCZ/strings.xml +++ b/core/src/main/res/values-cs-rCZ/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">Otevřít menu</string> <string name="drawer_close">Zavřít menu</string> <string name="drawer_preferences">Nastavení panelu</string> + <string name="drawer_feed_order_unplayed_episodes">Řadit dle počtu</string> + <string name="drawer_feed_order_alphabetical">Řadit abecedně</string> + <string name="drawer_feed_counter_new_unplayed">Počet nových a nepřehraných epizod</string> + <string name="drawer_feed_counter_new">Počet nových epizod</string> + <string name="drawer_feed_counter_unplayed">Počet nepřehraných epizod</string> + <string name="drawer_feed_counter_none">Žádné</string> <!--Webview actions--> <string name="open_in_browser_label">Otevřít v prohlížeči</string> <string name="copy_url_label">Kopírovat URL</string> @@ -38,6 +44,9 @@ <!--Other--> <string name="confirm_label">Potvrdit</string> <string name="cancel_label">Zrušit</string> + <string name="yes">Ano</string> + <string name="no">Ne +</string> <string name="author_label">Autor</string> <string name="language_label">Jazyk</string> <string name="url_label">URL</string> @@ -60,7 +69,13 @@ <string name="close_label">Zavřít</string> <string name="retry_label">Zkusit znovu</string> <string name="auto_download_label">Zahrnout do automaticky stahovaných</string> + <string name="auto_download_apply_to_items_title">Použít na předchozí epizody</string> + <string name="auto_download_apply_to_items_message">Nová funkce <i>auto stahování</i> bude automaticky použita na nové epizody.\nChcete ji také použít na předchozí epizody?</string> + <string name="auto_delete_label">Automaticky smazat epizodu\n(přeskočit globální nastavení)</string> <string name="parallel_downloads_suffix">\u0020paralelních stahování</string> + <string name="feed_auto_download_global">Globální</string> + <string name="feed_auto_download_always">Vždy</string> + <string name="feed_auto_download_never">Nikdy</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL kanálu</string> <string name="etxtFeedurlHint">URL nebo webová stránka kanálu</string> @@ -73,14 +88,20 @@ <string name="mark_all_read_msg">Všechny epizody označeny jako poslechnuté</string> <string name="mark_all_read_confirmation_msg">Prosím potvrďte, že chcete označit všechny vybrané epizody jako poslechnuté.</string> <string name="mark_all_read_feed_confirmation_msg">Prosím potvrďte, že chcete označit všechny epizody z tohoto zdroje jako poslechnuté.</string> + <string name="mark_all_seen_label">Označit vše jako zobrazené</string> <string name="show_info_label">Informace o zdroji</string> <string name="remove_feed_label">Odstranit podcast</string> + <string name="share_label">Sdílet...</string> <string name="share_link_label">Sdílet odkaz</string> - <string name="share_source_label">Sdílet adresu kanálu</string> + <string name="share_link_with_position_label">Sdílet odkaz s pozicí</string> + <string name="share_feed_url_label">Sdílet URL kanálu</string> + <string name="share_item_url_label">Sdílet URL epizody</string> + <string name="share_item_url_with_position_label">Sdílet URL epizody s pozicí</string> <string name="feed_delete_confirmation_msg">Prosím potvrďte, že chcete smazat tento kanál včetně všech stažených epizod.</string> <string name="feed_remover_msg">Odstranit kanál</string> <string name="load_complete_feed">Obnovit kompletní kanál</string> <string name="hide_episodes_title">Skrýt epizody</string> + <string name="episode_actions">Provést akce</string> <string name="hide_unplayed_episodes_label">Neposlechnuté</string> <string name="hide_paused_episodes_label">Pozastavené</string> <string name="hide_played_episodes_label">Poslechnuté</string> @@ -99,8 +120,8 @@ <string name="remove_label">Odstranit</string> <string name="remove_episode_lable">Odstranit epizodu</string> <string name="mark_read_label">Označit jako poslechnuté</string> - <string name="mark_unread_label">Označit jako neposlechnuté</string> <string name="marked_as_read_label">Označeno jako poslechnuté</string> + <string name="mark_unread_label">Označit jako neposlechnuté</string> <string name="add_to_queue_label">Přidat do fronty</string> <string name="added_to_queue_label">Přidáno do fronty</string> <string name="remove_from_queue_label">Odebrat z fronty</string> @@ -229,8 +250,12 @@ <string name="pref_smart_mark_as_played_title">Chytré označování jako poslechnuté</string> <string name="playback_pref">Přehrávání</string> <string name="network_pref">Síť</string> - <string name="pref_autoUpdateIntervall_title">Interval aktualizace zdrojů</string> - <string name="pref_autoUpdateIntervall_sum">Udává interval, ve kterém se kanály automaticky aktualizují nebo tuto funkci deaktivuje</string> + <string name="pref_autoUpdateIntervallOrTime_title">Aktualizovat interval nebo čas v průběhu dne</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Udat interval nebo přesný čas v průběhu dne pro automatickou aktualizaci kanálů</string> + <string name="pref_autoUpdateIntervallOrTime_message">Můžete nastavit <i>interval</i> jako třeba \"každé 2 hodiny\", nastavit specifický <i>čas v průběhu dne</i> jako \"7:00\" nebo úplně <i>vypnout</i> automatické aktualizace.\n\n<small>Mějte na paměti: Časy aktualizací nejsou přesné. Možná zaznamenáte krátká zpoždění.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Vypnout</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Nastavit interval</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Nastavit čas v průběhu dne</string> <string name="pref_downloadMediaOnWifiOnly_sum">Stahovat soubory pouze pomocí WiFi</string> <string name="pref_followQueue_title">Kontinuální přehrávání</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi stahování</string> @@ -250,8 +275,14 @@ <string name="pref_auto_flattr_sum">Nastavit automatické flattrování</string> <string name="user_interface_label">Uživatelské rozhraní</string> <string name="pref_set_theme_title">Vybrat motiv</string> + <string name="pref_nav_drawer_title">Upravit navigační panel</string> + <string name="pref_nav_drawer_sum">Upravit vzhled navigačního panelu.</string> <string name="pref_nav_drawer_items_title">Změnit navigační panel</string> <string name="pref_nav_drawer_items_sum">Upravit zobrazení položek v navigačním panelu.</string> + <string name="pref_nav_drawer_feed_order_title">Nastavit pořadí sbírek</string> + <string name="pref_nav_drawer_feed_order_sum">Upravit pořadí vašich sbírek</string> + <string name="pref_nav_drawer_feed_counter_title">Nastavit počítadlo sbírek</string> + <string name="pref_nav_drawer_feed_counter_sum">Upravit informaci zobrazovanou počítadlem sbírek</string> <string name="pref_set_theme_sum">Změnit vzhled AntennaPod.</string> <string name="pref_automatic_download_title">Automatické stahování</string> <string name="pref_automatic_download_sum">Nastavení automatického stahování epizod.</string> @@ -283,10 +314,14 @@ <string name="pref_expandNotify_sum">Vždy zobrazovat tlačítka pro přehrávání v upozornění.</string> <string name="pref_persistNotify_title">Pevné ovládání přehrávání</string> <string name="pref_persistNotify_sum">Zachovat upozornění a ovládání na obrazovce uzamčení i při pozastaveném přehrávání.</string> + <string name="pref_showDownloadReport_title">Zobrazit report stahování</string> + <string name="pref_showDownloadReport_sum">Pokud selže stahování, vygenerovat report zobrazující detaily o chybě.</string> <string name="pref_expand_notify_unsupport_toast">Verze Androidu nižší než 4.1 nepodporují rozšířená upozornění.</string> <string name="pref_queueAddToFront_sum">Přidávat nové epizody na začátek fronty.</string> <string name="pref_queueAddToFront_title">Přidat na začátek.</string> <string name="pref_smart_mark_as_played_disabled">Vypnuto</string> + <string name="pref_image_cache_size_title">Velikost odkládací paměti obrázků</string> + <string name="pref_image_cache_size_sum">Velikost diskové paměti pro obrázky.</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Povolit automatické flattrování</string> <string name="auto_flattr_after_percent">Flattrovat díl jakmile bude odehráno %d procent</string> @@ -312,6 +347,7 @@ <string name="opml_import_error_dir_empty">Adresář importu je prázdný.</string> <string name="select_all_label">Označit vše</string> <string name="deselect_all_label">Zrušit výběr</string> + <string name="select_options_label">Vybrat ...</string> <string name="choose_file_from_filesystem">Z místního souborového systému</string> <string name="choose_file_from_external_application">Použít externí aplikaci</string> <string name="opml_export_label">OPML export</string> @@ -326,9 +362,24 @@ <string name="sleep_timer_label">Časovač vypnutí</string> <string name="time_left_label">Zbývající čas:\u0020</string> <string name="time_dialog_invalid_input">Neplatný vstup, musí být zadáno celé číslo</string> - <string name="time_unit_seconds">sekund</string> - <string name="time_unit_minutes">minut</string> - <string name="time_unit_hours">hodin</string> + <string name="time_seconds">sekund</string> + <string name="time_minutes">minut</string> + <string name="time_hours">hodin</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 sekunda</item> + <item quantity="few">%d sekundy</item> + <item quantity="other">%d sekund</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minuta</item> + <item quantity="few">%d minuty</item> + <item quantity="other">%d minut</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 hodina</item> + <item quantity="few">%d hodiny</item> + <item quantity="other">%d hodin</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORIE</string> <string name="gpodnet_toplist_header">TOP PODCASTY</string> @@ -373,6 +424,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">Automatické pozastavení přehrávání</string> <string name="pref_resumeAfterCall_sum">Pokračovat v přehrávání po ukončení telefonního hovoru</string> <string name="pref_resumeAfterCall_title">Pokračovat po telefonátu</string> + <string name="pref_restart_required">Pro aktivování změn nastavení bylo třeba restartovat aplikaci AntennaPod.</string> <!--Online feed view--> <string name="subscribe_label">Odebírat</string> <string name="subscribed_label">Odebíráno</string> @@ -399,7 +451,29 @@ <!--Feed information screen--> <string name="authentication_label">Ověření</string> <string name="authentication_descr">Změnit uživatelské jméno a heslo pro tento podcast a jeho epizody.</string> + <!--Progress information--> + <string name="progress_upgrading_database">Probíhá aktualizace databáze</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importuji odběry z jednoúčelových aplikací...</string> <string name="search_itunes_label">Prohledat iTunes</string> + <string name="select_label"><b>Vybrat ...</b></string> + <string name="all_label">Vše</string> + <string name="selected_all_label">Vybrány všechny epizody</string> + <string name="none_label">Žádné</string> + <string name="deselected_all_label">Odebrány všechny epizody</string> + <string name="played_label">Přehrány</string> + <string name="selected_played_label">Vybrány přehrané epizody</string> + <string name="unplayed_label">Nepřehrány</string> + <string name="selected_unplayed_label">Odebrány nepřehrané epizody</string> + <string name="downloaded_label">Stažené</string> + <string name="selected_downloaded_label">Vybrány stažené epizody</string> + <string name="not_downloaded_label">Nestažené</string> + <string name="selected_not_downloaded_label">Vybrány nestažené epizody</string> + <string name="sort_title"><b>Řadit podle ...</b></string> + <string name="sort_title_a_z">Názvu (A \u2192 Z)</string> + <string name="sort_title_z_a">Názvu (Z \u2192 A)</string> + <string name="sort_date_new_old">Data (Nové \u2192 Staré)</string> + <string name="sort_date_old_new">Data (Staré \u2192 Nové)</string> + <string name="sort_duration_short_long">Délka (Krátké \u2192 Dlouhé)</string> + <string name="sort_duration_long_short">Délka (Dlouhé \u2192 Krátké)</string> </resources> diff --git a/core/src/main/res/values-da/strings.xml b/core/src/main/res/values-da/strings.xml index ba7fafca6..cc18185ec 100644 --- a/core/src/main/res/values-da/strings.xml +++ b/core/src/main/res/values-da/strings.xml @@ -71,7 +71,6 @@ <string name="show_info_label">Vis information</string> <string name="remove_feed_label">Fjern podcast</string> <string name="share_link_label">Del webside link</string> - <string name="share_source_label">Del feed link</string> <string name="feed_delete_confirmation_msg">Bekræft venligst at du vil fjerne dette feed og ALLE episoder du har downloadet fra dette feed.</string> <string name="feed_remover_msg">Fjerner feed</string> <string name="load_complete_feed">Opdater hele feed\'et</string> @@ -197,8 +196,6 @@ <string name="pref_auto_delete_title">Slet Automatisk</string> <string name="playback_pref">Afspilning</string> <string name="network_pref">Netværk</string> - <string name="pref_autoUpdateIntervall_title">Opdaterings interval</string> - <string name="pref_autoUpdateIntervall_sum">Specificer et interval indenfor hvilket feeds opdaterer automatisk eller deaktiver det</string> <string name="pref_downloadMediaOnWifiOnly_sum">Download kun medie filer over WiFi</string> <string name="pref_followQueue_title">Kontinuerlig afspilning</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi medie download</string> @@ -240,8 +237,6 @@ <string name="pref_gpodnet_setlogin_information_sum">Skift din gpodder.net kontos login information.</string> <string name="pref_playback_speed_title">Afspilningshastigheder</string> <string name="pref_playback_speed_sum">Tilpas tilgængelige hastigheder for variabelt afspilningshastigheds plugin</string> - <string name="pref_seek_delta_title">Søg tid</string> - <string name="pref_seek_delta_sum">Søg så mange sekunder når der spoles tilbage eller frem</string> <string name="pref_gpodnet_sethostname_title">Indstil værtsnavn</string> <string name="pref_gpodnet_sethostname_use_default_host">Brug standard vært</string> <string name="pref_expandNotify_title">Udvid notifikation</string> @@ -283,9 +278,6 @@ <string name="sleep_timer_label">Søvn timer</string> <string name="time_left_label">Tid tilbage:\u0020</string> <string name="time_dialog_invalid_input">Ugyldig indtastning, tid skal være et heltal</string> - <string name="time_unit_seconds">sekunder</string> - <string name="time_unit_minutes">minutter</string> - <string name="time_unit_hours">timer</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORIER </string> <string name="gpodnet_toplist_header">TOP PODCASTS</string> @@ -354,6 +346,7 @@ <!--Feed information screen--> <string name="authentication_label">Godkendelse</string> <string name="authentication_descr">Skift dit brugernavn og kodeord for denne podcast og dets episoder.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importerer abonnementer fra single-purpose apps…</string> </resources> diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml index 93e52acb6..47a0d7877 100644 --- a/core/src/main/res/values-de/strings.xml +++ b/core/src/main/res/values-de/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">Menü öffnen</string> <string name="drawer_close">Menü schließen</string> <string name="drawer_preferences">Seitenleisten-Einstellungen</string> + <string name="drawer_feed_order_unplayed_episodes">Sortieren nach Zähler</string> + <string name="drawer_feed_order_alphabetical">Alphabetisch sortieren</string> + <string name="drawer_feed_counter_new_unplayed">Anzahl neuer und ungespielter Episoden</string> + <string name="drawer_feed_counter_new">Anzahl neuer Episoden</string> + <string name="drawer_feed_counter_unplayed">Anzahl ungespielter Episoden</string> + <string name="drawer_feed_counter_none">Keine</string> <!--Webview actions--> <string name="open_in_browser_label">Im Browser öffnen</string> <string name="copy_url_label">URL kopieren</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label">Bestätigen</string> <string name="cancel_label">Abbrechen</string> + <string name="yes">Ja</string> + <string name="no">Nein</string> <string name="author_label">Autor</string> <string name="language_label">Sprache</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">Schließen</string> <string name="retry_label">Erneut versuchen</string> <string name="auto_download_label">Automatisch herunterladen</string> + <string name="auto_download_apply_to_items_title">Auf bisherige Episoden anwenden</string> + <string name="auto_download_apply_to_items_message">Die neue Einstellung zum <i>Automatischen Download</i> wird automatisch auf neue Episoden angewandt.\nMöchtest du sie auch auf die bisherige Episoden anwenden?</string> + <string name="auto_delete_label">Episoden automatisch löschen\n(überschreibt globale Vorgabe)</string> <string name="parallel_downloads_suffix">\u0020gleichzeitige Downloads</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Immer</string> + <string name="feed_auto_download_never">Nie</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">Feed URL</string> <string name="etxtFeedurlHint">URL des Feeds oder der Webseite</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">Alle Episoden als gespielt markieren</string> <string name="mark_all_read_confirmation_msg">Bitte bestätige, dass alle Episoden als gespielt markiert werden sollen.</string> <string name="mark_all_read_feed_confirmation_msg">Bitte bestätige, dass alle Episoden in diesem Feed als gespielt markiert werden sollen.</string> + <string name="mark_all_seen_label">Alle als gesehen markieren</string> <string name="show_info_label">Informationen anzeigen</string> <string name="remove_feed_label">Podcast entfernen</string> + <string name="share_label">Teile...</string> <string name="share_link_label">Webseiten-Link teilen</string> - <string name="share_source_label">Feed-Link teilen</string> + <string name="share_link_with_position_label">Teile Link mit Zeitmarke</string> + <string name="share_feed_url_label">Teile URL des Podcasts</string> + <string name="share_item_url_label">Teile URL der Episode</string> + <string name="share_item_url_with_position_label">Teile URL der Episode mit Zeitmarke</string> <string name="feed_delete_confirmation_msg">Bitte bestätige, dass du diesen Feed und ALLE heruntergeladenen Episoden dieses Feeds entfernen möchtest.</string> <string name="feed_remover_msg">Entferne Feed</string> <string name="load_complete_feed">Kompletten Feed aktualisieren</string> <string name="hide_episodes_title">Episoden verbergen</string> + <string name="episode_actions">Aktionen anwenden</string> <string name="hide_unplayed_episodes_label">Ungespielt</string> <string name="hide_paused_episodes_label">Pausiert</string> <string name="hide_played_episodes_label">Gespielt</string> @@ -99,8 +119,8 @@ <string name="remove_label">Entfernen</string> <string name="remove_episode_lable">Episode entfernen</string> <string name="mark_read_label">Als gespielt markieren</string> - <string name="mark_unread_label">Als ungespielt markieren</string> <string name="marked_as_read_label">Als gespielt markiert</string> + <string name="mark_unread_label">Als ungespielt markieren</string> <string name="add_to_queue_label">Zur Abspielliste hinzufügen</string> <string name="added_to_queue_label">Zur Abspielliste hinzugefügt</string> <string name="remove_from_queue_label">Aus der Abspielliste entfernen</string> @@ -229,8 +249,12 @@ <string name="pref_smart_mark_as_played_title">Schlaues als gespielt markieren</string> <string name="playback_pref">Wiedergabe</string> <string name="network_pref">Netzwerk</string> - <string name="pref_autoUpdateIntervall_title">Aktualisierungsintervall</string> - <string name="pref_autoUpdateIntervall_sum">Lege ein Intervall fest, in dem Feeds automatisch aktualisiert werden oder deaktiviere es</string> + <string name="pref_autoUpdateIntervallOrTime_title">Aktualisierungsintervall oder -tageszeit</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Lege ein Intervall oder eine Tageszeit zur automatischen Aktualisierung der Podcasts fest</string> + <string name="pref_autoUpdateIntervallOrTime_message">Du kannst ein festes <i>Intervall</i> wie \"alle 2 Stunden\", eine bestimmte <i>Tageszeit</i> wie \"7 Uhr morgens\" festlegen oder die automatische Aktualisierung komplett <i>deaktivieren</i>.\n\n<small>Bitte beachte: Der Zeitpunkt der Aktualisierung ist ungenau. Du wirst vielleicht eine kurze Verzögerung bemerken.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Deaktivieren</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Intervall einstellen</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Tageszeit festlegen</string> <string name="pref_downloadMediaOnWifiOnly_sum">Lade Mediendateien nur über WiFi</string> <string name="pref_followQueue_title">Durchgehendes Abspielen</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi Medien-Download</string> @@ -250,8 +274,14 @@ <string name="pref_auto_flattr_sum">Automatisches Flattrn konfigurieren</string> <string name="user_interface_label">Benutzeroberfläche</string> <string name="pref_set_theme_title">Theme auswählen</string> + <string name="pref_nav_drawer_title">Seitenleiste anpassen</string> + <string name="pref_nav_drawer_sum">Passe das Aussehen der Seitenleiste an.</string> <string name="pref_nav_drawer_items_title">Seitenleiste ändern</string> <string name="pref_nav_drawer_items_sum">Ändere, welche Listen in der Seitenleiste erscheinen</string> + <string name="pref_nav_drawer_feed_order_title">Reihenfolge der Abonnements einstellen</string> + <string name="pref_nav_drawer_feed_order_sum">Ändere die Reihenfolge deiner Abonnements</string> + <string name="pref_nav_drawer_feed_counter_title">Abonnement-Zähler einstellen</string> + <string name="pref_nav_drawer_feed_counter_sum">Ändere, welche Information der Abonnement-Zähler anzeigt</string> <string name="pref_set_theme_sum">Ändere das Aussehen von AntennaPod.</string> <string name="pref_automatic_download_title">Automatisches Herunterladen</string> <string name="pref_automatic_download_sum">Konfiguriere das automatische Herunterladen von Episoden.</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">Erweiterte Wiedergabebenachrichtigung mit Abspiel-, Pause- und Stop-Knöpfen anzeigen.</string> <string name="pref_persistNotify_title">Persistente Wiedergabesteurung</string> <string name="pref_persistNotify_sum">Zeige Wiedergabebedienelemente in der Benachrichtigung und im Lockscreen an, während die Wiedergabe pausiert ist.</string> + <string name="pref_showDownloadReport_title">Zeige Download-Bericht</string> + <string name="pref_showDownloadReport_sum">Wenn Downloads fehlschlagen, erstelle einen Bericht, der die Details des Fehlschlages beschreibt.</string> <string name="pref_expand_notify_unsupport_toast">Android-Versionen vor 4.1 unterstützen keine erweiterten Benachrichtigungen.</string> <string name="pref_queueAddToFront_sum">Fügen Sie neue Folgen auf den Anfang der Warteschlange.</string> <string name="pref_queueAddToFront_title">Vorne in Abspielliste einreihen</string> <string name="pref_smart_mark_as_played_disabled">Deaktiviert</string> + <string name="pref_image_cache_size_title">Größe des Bilder-Zwischenspeichers</string> + <string name="pref_image_cache_size_sum">Größe des Zwischenspeichers für Bilder</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Automatisches Flattrn aktivieren</string> <string name="auto_flattr_after_percent">Flattr eine Episode, sobald %d Prozent gespielt worden sind</string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">Der Import-Ordner ist leer.</string> <string name="select_all_label">Alle auswählen</string> <string name="deselect_all_label">Auswahl zurücksetzen</string> + <string name="select_options_label">Wähle aus ...</string> <string name="choose_file_from_filesystem">Vom lokalen Dateisystem</string> <string name="choose_file_from_external_application">Verwende externe Anwendung</string> <string name="opml_export_label">OPML Export</string> @@ -326,9 +361,21 @@ <string name="sleep_timer_label">Schlummerfunktion</string> <string name="time_left_label">Zeit übrig:\u0020</string> <string name="time_dialog_invalid_input">Ungültige Eingabe, Zeit muss eine Ganzzahl sein</string> - <string name="time_unit_seconds">Sekunden</string> - <string name="time_unit_minutes">Minuten</string> - <string name="time_unit_hours">Stunden</string> + <string name="time_seconds">Sekunden</string> + <string name="time_minutes">Minuten</string> + <string name="time_hours">Stunden</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 Sekunde</item> + <item quantity="other">%d Sekunden</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 Minute</item> + <item quantity="other">%d Minuten</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 Stunde</item> + <item quantity="other">%d Stunden</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORIEN</string> <string name="gpodnet_toplist_header">BESTE PODCASTS</string> @@ -373,6 +420,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">Bei Unterbrechungen pausieren</string> <string name="pref_resumeAfterCall_sum">Wiedergabe fortsetzen, wenn Anruf beendet ist</string> <string name="pref_resumeAfterCall_title">Nach Anruf fortsetzen</string> + <string name="pref_restart_required">AntennaPod muss neu gestartet werden, damit die Änderungen wirksam werden.</string> <!--Online feed view--> <string name="subscribe_label">Abonnieren</string> <string name="subscribed_label">Abonniert</string> @@ -399,7 +447,29 @@ <!--Feed information screen--> <string name="authentication_label">Authentifizierung</string> <string name="authentication_descr">Ändere den Benutzernamen und das Passwort für diesen Podcast und dessen Episoden.</string> + <!--Progress information--> + <string name="progress_upgrading_database">Datenbank wird aktualisiert</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importiere Abonnements aus Single-Purpose Apps</string> <string name="search_itunes_label">iTunes durchsuchen</string> + <string name="select_label"><b>Wähle aus ...</b></string> + <string name="all_label">Alle</string> + <string name="selected_all_label">Alle Episoden ausgewählt</string> + <string name="none_label">Keine</string> + <string name="deselected_all_label">Alle Episoden abgewählt</string> + <string name="played_label">Gespielt</string> + <string name="selected_played_label">Gespielte Episoden ausgewählt</string> + <string name="unplayed_label">Ungespielt</string> + <string name="selected_unplayed_label">Ungespielte Episoden ausgewählt</string> + <string name="downloaded_label">Heruntergeladen</string> + <string name="selected_downloaded_label">Heruntergeladene Episoden ausgewählt</string> + <string name="not_downloaded_label">Nicht heruntergeladen</string> + <string name="selected_not_downloaded_label">Nicht heruntergeladene Episoden ausgewählt</string> + <string name="sort_title"><b>Sortieren nach ...</b></string> + <string name="sort_title_a_z">Titel (A \u2192 Z)</string> + <string name="sort_title_z_a">Titel (Z \u2192 A)</string> + <string name="sort_date_new_old">Datum (neu \u2192 alt)</string> + <string name="sort_date_old_new">Datum (alt \u2192 neu)</string> + <string name="sort_duration_short_long">Dauer (kurz \u2192 lang)</string> + <string name="sort_duration_long_short">Dauer (lang \u2192 kurz)</string> </resources> diff --git a/core/src/main/res/values-es-rES/strings.xml b/core/src/main/res/values-es-rES/strings.xml index d05c34876..2607d1794 100644 --- a/core/src/main/res/values-es-rES/strings.xml +++ b/core/src/main/res/values-es-rES/strings.xml @@ -42,7 +42,6 @@ <string name="mark_all_read_label">Marcar todo como leído</string> <string name="show_info_label">Información del programa</string> <string name="share_link_label">Compartir el enlace de la web</string> - <string name="share_source_label">Compartir el enlace del canal</string> <string name="feed_delete_confirmation_msg">Confirme que quiere eliminar este canal y TODOS los episodios descargados del mismo.</string> <!--actions on feeditems--> <string name="download_label">Descargar</string> @@ -125,8 +124,6 @@ <string name="pref_followQueue_sum">Saltar al siguiente elemento de la cola al acabar la reproducción</string> <string name="playback_pref">Reproducción</string> <string name="network_pref">Red</string> - <string name="pref_autoUpdateIntervall_title">Intervalo de actualización</string> - <string name="pref_autoUpdateIntervall_sum">Especificar el intervalo en que se actualizarán automáticamente los canales, o desactivarlo</string> <string name="pref_downloadMediaOnWifiOnly_sum">Solo descargar los contenidos por WiFi</string> <string name="pref_followQueue_title">Reproducción continua</string> <string name="pref_downloadMediaOnWifiOnly_title">Descarga de contenidos por WiFi</string> @@ -193,5 +190,6 @@ <!--Online feed view--> <!--Content descriptions for image buttons--> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> </resources> diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml index d5255a589..6670836db 100644 --- a/core/src/main/res/values-es/strings.xml +++ b/core/src/main/res/values-es/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">Abrir menú</string> <string name="drawer_close">Cerrar menú</string> <string name="drawer_preferences">Preferencias del cajón</string> + <string name="drawer_feed_order_unplayed_episodes">Ordenar por cuenta</string> + <string name="drawer_feed_order_alphabetical">Ordenar alfabéticamente</string> + <string name="drawer_feed_counter_new_unplayed">Cantidad de episodios nuevos y no escuchados</string> + <string name="drawer_feed_counter_new">Cantidad de episodios nuevos</string> + <string name="drawer_feed_counter_unplayed">Cantidad de episodios no escuchados</string> + <string name="drawer_feed_counter_none">Ninguno</string> <!--Webview actions--> <string name="open_in_browser_label">Abrir en el navegador</string> <string name="copy_url_label">Copiar URL</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label">Confirmar</string> <string name="cancel_label">Cancelar</string> + <string name="yes">Sí</string> + <string name="no">No</string> <string name="author_label">Autor</string> <string name="language_label">Idioma</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">Cerrar</string> <string name="retry_label">Reintentar</string> <string name="auto_download_label">Incluir en descargas automáticas</string> + <string name="auto_download_apply_to_items_title">Aplicar a episodios anteriores</string> + <string name="auto_download_apply_to_items_message">La nueva opción <i>Auto Descarga</i> se aplicará automáticamente a episodios nuevos.\n¿También desea aplicarlo a episodios anteriores?</string> + <string name="auto_delete_label">Auto borrar episodio\n(Ignorando los ajustes globales)</string> <string name="parallel_downloads_suffix">\u0020descargas paralelas</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Siempre</string> + <string name="feed_auto_download_never">Nunca</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL del canal</string> <string name="etxtFeedurlHint">URL del canal o del sitio web</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">Se marcaron todos los episodios como escuchados</string> <string name="mark_all_read_confirmation_msg">Por favor, confirme que desea marcar todos los episodios como escuchados.</string> <string name="mark_all_read_feed_confirmation_msg">Por favor, confirme que desea marcar todos los episodios de este feed como escuchados.</string> + <string name="mark_all_seen_label">Marcar todos como vistos</string> <string name="show_info_label">Información del programa</string> <string name="remove_feed_label">Eliminar podcast</string> + <string name="share_label">Compartir...</string> <string name="share_link_label">Compartir el enlace de la web</string> - <string name="share_source_label">Compartir el enlace del canal</string> + <string name="share_link_with_position_label">Compartir enlace con posición</string> + <string name="share_feed_url_label">Compartir URL del canal</string> + <string name="share_item_url_label">Compartir URL del episodio</string> + <string name="share_item_url_with_position_label">Compartir URL del episodio con posición</string> <string name="feed_delete_confirmation_msg">Confirme que quiere eliminar este canal y TODOS los episodios descargados del mismo.</string> <string name="feed_remover_msg">Quitando el canal</string> <string name="load_complete_feed">Actualizar el canal completo</string> <string name="hide_episodes_title">Ocultar episodios</string> + <string name="episode_actions">Aplicar acciones</string> <string name="hide_unplayed_episodes_label">No escuchados</string> <string name="hide_paused_episodes_label">Pausados</string> <string name="hide_played_episodes_label">Escuchados</string> @@ -99,8 +119,8 @@ <string name="remove_label">Quitar</string> <string name="remove_episode_lable">Quitar episodio</string> <string name="mark_read_label">Marcar como escuchado</string> - <string name="mark_unread_label">Marcar como no escuchado</string> <string name="marked_as_read_label">Marcado como escuchado</string> + <string name="mark_unread_label">Marcar como no escuchado</string> <string name="add_to_queue_label">Añadir a la cola</string> <string name="added_to_queue_label">Añadido a la cola</string> <string name="remove_from_queue_label">Quitar de la cola</string> @@ -229,8 +249,12 @@ <string name="pref_smart_mark_as_played_title">Marcar como escuchado inteligente</string> <string name="playback_pref">Reproducción</string> <string name="network_pref">Red</string> - <string name="pref_autoUpdateIntervall_title">Intervalo de actualización</string> - <string name="pref_autoUpdateIntervall_sum">Especificar el intervalo en que se actualizarán automáticamente los canales, o desactivarlo</string> + <string name="pref_autoUpdateIntervallOrTime_title">Intervalo de actualización u hora del día</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Especificar el intervalo o la hora del día en que se actualizarán automáticamente los canales</string> + <string name="pref_autoUpdateIntervallOrTime_message">Se puede ajustar un <i>intervalo</i> como \"cada 2 horas\", especificar una <i>hora del día</i> como \"7:00 AM\" o <i>deshabilitar</i> las actualizaciones automáticas.\n\n<small>Nota: Las horas de actualización no son exactas. Puede haber un ligero retraso.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Deshabilitar</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Ajustar intervalo</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Ajustar hora del día</string> <string name="pref_downloadMediaOnWifiOnly_sum">Solo descargar los contenidos por WiFi</string> <string name="pref_followQueue_title">Reproducción continua</string> <string name="pref_downloadMediaOnWifiOnly_title">Descarga de contenidos por WiFi</string> @@ -250,8 +274,14 @@ <string name="pref_auto_flattr_sum">Configurar flattr automático</string> <string name="user_interface_label">Interfaz de usuario</string> <string name="pref_set_theme_title">Elegir un tema</string> + <string name="pref_nav_drawer_title">Personalizar el cajón de navegación</string> + <string name="pref_nav_drawer_sum">Personalizar la apariencia del cajón de navegación</string> <string name="pref_nav_drawer_items_title">Cambiar el cajón de navegación</string> <string name="pref_nav_drawer_items_sum">Cambiar los ítems que aparecen en el cajón de navegación</string> + <string name="pref_nav_drawer_feed_order_title">Ajustar orden de suscripción</string> + <string name="pref_nav_drawer_feed_order_sum">Cambiar el orden de las suscripciones</string> + <string name="pref_nav_drawer_feed_counter_title">Ajustar contador de suscripción</string> + <string name="pref_nav_drawer_feed_counter_sum">Cambiar la información mostrada en el contador de suscripción</string> <string name="pref_set_theme_sum">Cambiar la apariencia de AntennaPod.</string> <string name="pref_automatic_download_title">Descarga automática</string> <string name="pref_automatic_download_sum">Configurar la descarga automática de episodios.</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">Expandir siempre la notificación para mostrar los botones de reproducción</string> <string name="pref_persistNotify_title">Controles de reproducción persistentes</string> <string name="pref_persistNotify_sum">Mantener la notificación y controles en pantalla de bloqueo cuando se pausa.</string> + <string name="pref_showDownloadReport_title">Mostrar informe de descarga</string> + <string name="pref_showDownloadReport_sum">Si la descarga falla, generar un informe con los detalles del fallo</string> <string name="pref_expand_notify_unsupport_toast">Las versiones de Android anteriores a la 4.1 no soportan notificaciones expandidas</string> <string name="pref_queueAddToFront_sum">Agregar nuevos episodios al principio de la cola.</string> <string name="pref_queueAddToFront_title">Poner al principio de la cola.</string> <string name="pref_smart_mark_as_played_disabled">Deshabilitado</string> + <string name="pref_image_cache_size_title">Tamaño de la caché de imágenes</string> + <string name="pref_image_cache_size_sum">Tamaño de la caché en disco para imágenes.</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Habilitar Flattr automático</string> <string name="auto_flattr_after_percent">Hacer Flattr del episodio en cuanto se haya reproducido el %d por ciento</string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">El directorio de importación está vacío.</string> <string name="select_all_label">Seleccionar todo</string> <string name="deselect_all_label">Deseleccionar todo</string> + <string name="select_options_label">Seleccionar...</string> <string name="choose_file_from_filesystem">Desde el sistema de ficheros local</string> <string name="choose_file_from_external_application">Usar aplicación externa</string> <string name="opml_export_label">Exportar a OPML</string> @@ -326,9 +361,21 @@ <string name="sleep_timer_label">Temporizador</string> <string name="time_left_label">Tiempo restante:\u0020</string> <string name="time_dialog_invalid_input">Entrada no válida, el tiempo debe ser un entero</string> - <string name="time_unit_seconds">segundos</string> - <string name="time_unit_minutes">minutos</string> - <string name="time_unit_hours">horas</string> + <string name="time_seconds">segundos</string> + <string name="time_minutes">minutos</string> + <string name="time_hours">horas</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 segundo</item> + <item quantity="other">%d segundos</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minuto</item> + <item quantity="other">%d minutos</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 hora</item> + <item quantity="other">%d horas</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">CATEGORÍAS</string> <string name="gpodnet_toplist_header">MEJORES PODCASTS</string> @@ -373,6 +420,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">Pausar durante las interrupciones</string> <string name="pref_resumeAfterCall_sum">Reanudar reproducción tras una llamada</string> <string name="pref_resumeAfterCall_title">Reanudar tras una llamada</string> + <string name="pref_restart_required">Es necesario reiniciar AntennaPod para aplicar los cambios.</string> <!--Online feed view--> <string name="subscribe_label">Suscribirse</string> <string name="subscribed_label">Suscrito</string> @@ -399,7 +447,29 @@ <!--Feed information screen--> <string name="authentication_label">Autenticación</string> <string name="authentication_descr">Cambiar nombre y contraseña de este podcast y sus episodios</string> + <!--Progress information--> + <string name="progress_upgrading_database">Actualizando la base de datos</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importando subscripciones de aplicaciones de uso específico...</string> <string name="search_itunes_label">Buscar en iTunes</string> + <string name="select_label"><b>Seleccionar...</b></string> + <string name="all_label">Todo</string> + <string name="selected_all_label">Seleccionados todos los episodios</string> + <string name="none_label">Ninguno</string> + <string name="deselected_all_label">Deseleccionados todos los episodios</string> + <string name="played_label">Reproducido</string> + <string name="selected_played_label">Seleccionados episodios reproducidos</string> + <string name="unplayed_label">No reproducidos</string> + <string name="selected_unplayed_label">Seleccionados episodios no reproducidos</string> + <string name="downloaded_label">Descargado</string> + <string name="selected_downloaded_label">Seleccionados episodios descargados</string> + <string name="not_downloaded_label">No descargado</string> + <string name="selected_not_downloaded_label">Seleccionados episodios no descargados</string> + <string name="sort_title"><b>Ordenar por...</b></string> + <string name="sort_title_a_z">Título (A \u2192 Z)</string> + <string name="sort_title_z_a">Título (Z \u2192 A)</string> + <string name="sort_date_new_old">Fecha (Nuevo \u2192 Antiguo)</string> + <string name="sort_date_old_new">Fecha (Antiguo \u2192 Nuevo)</string> + <string name="sort_duration_short_long">Duración (Corto \u2192 Largo)</string> + <string name="sort_duration_long_short">Duración (Largo \u2192 Corto)</string> </resources> diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml index 2f59e139d..10d3fe9a4 100644 --- a/core/src/main/res/values-fr/strings.xml +++ b/core/src/main/res/values-fr/strings.xml @@ -26,6 +26,12 @@ <!--Main activity--> <string name="drawer_open">Ouvrir le menu</string> <string name="drawer_close">Fermer le menu</string> + <string name="drawer_feed_order_unplayed_episodes">Trier par compteur</string> + <string name="drawer_feed_order_alphabetical">Trier alphabétiquement</string> + <string name="drawer_feed_counter_new_unplayed">Nombre de nouveaux épisodes non-lus</string> + <string name="drawer_feed_counter_new">Nombre de nouveaux épisodes</string> + <string name="drawer_feed_counter_unplayed">Nombre d\'épisodes non-lus</string> + <string name="drawer_feed_counter_none">Aucun</string> <!--Webview actions--> <string name="open_in_browser_label">Ouvrir dans le navigateur</string> <string name="copy_url_label">Copier l\'URL</string> @@ -37,8 +43,11 @@ <!--Other--> <string name="confirm_label">Confirmer</string> <string name="cancel_label">Annuler</string> + <string name="yes">Oui</string> + <string name="no">Non</string> <string name="author_label">Auteur</string> <string name="language_label">Langue</string> + <string name="url_label">URL</string> <string name="podcast_settings_label">Préférences</string> <string name="cover_label">Image</string> <string name="error_label">Erreur</string> @@ -58,7 +67,11 @@ <string name="close_label">Fermer</string> <string name="retry_label">Réessayer</string> <string name="auto_download_label">Télécharger automatiquement à l\'avenir</string> + <string name="auto_download_apply_to_items_title">Appliquer aux épisodes précédents</string> <string name="parallel_downloads_suffix">\u0020téléchargements parallèles</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Toujours</string> + <string name="feed_auto_download_never">Jamais</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL du flux</string> <string name="etxtFeedurlHint">URL ou flux ou site web</string> @@ -71,13 +84,29 @@ <string name="mark_all_read_msg">Tous les épisodes ont été marqués comme lus</string> <string name="mark_all_read_confirmation_msg">Veuillez confirmer que vous voulez bien marquer tous les épisodes comme lus</string> <string name="mark_all_read_feed_confirmation_msg">Veuillez confirmer que vous voulez bien marquer tous les épisode de ce flux comme lus</string> + <string name="mark_all_seen_label">Marquer tout comme vu</string> <string name="show_info_label">Voir les détails</string> <string name="remove_feed_label">Supprimer le podcast</string> + <string name="share_label">Partager...</string> <string name="share_link_label">Partager un lien vers le site</string> - <string name="share_source_label">Partager le flux</string> + <string name="share_link_with_position_label">Partager lien avec position</string> + <string name="share_feed_url_label">Partager lien du flux</string> + <string name="share_item_url_label">Partager lien de l\'épisode</string> + <string name="share_item_url_with_position_label">Partager lien de l\'épisode avec position</string> <string name="feed_delete_confirmation_msg">Veuillez confirmer que vous voulez bien supprimer ce flux et TOUS ses épisodes que vous avez téléchargés.</string> <string name="feed_remover_msg">Flux en cours de suppression</string> <string name="load_complete_feed">Mettre à jour tout le flux</string> + <string name="hide_episodes_title">Cacher épisodes</string> + <string name="episode_actions">Appliquer les actions</string> + <string name="hide_unplayed_episodes_label">Non joués</string> + <string name="hide_paused_episodes_label">En pause</string> + <string name="hide_played_episodes_label">Joués</string> + <string name="hide_queued_episodes_label">Rajouté à la liste</string> + <string name="hide_not_queued_episodes_label">Pas rajouté à la liste</string> + <string name="hide_downloaded_episodes_label">Téléchargé</string> + <string name="hide_not_downloaded_episodes_label">Non téléchargé</string> + <string name="filtered_label">Filtré</string> + <string name="refresh_failed_msg">{fa-exclamation-circle} Dernière mise à jour échouée</string> <!--actions on feeditems--> <string name="download_label">Télécharger</string> <string name="play_label">Lire</string> @@ -87,15 +116,18 @@ <string name="remove_label">Supprimer</string> <string name="remove_episode_lable">Supprimer cet épisode</string> <string name="mark_read_label">Marquer comme lu</string> - <string name="mark_unread_label">Marquer comme non lu</string> <string name="marked_as_read_label">Les épisodes ont été marqués comme lus</string> + <string name="mark_unread_label">Marquer comme non lu</string> <string name="add_to_queue_label">Ajouter à la liste</string> + <string name="added_to_queue_label">Ajouté à la liste</string> <string name="remove_from_queue_label">Supprimer de la liste</string> <string name="visit_website_label">Visiter le site</string> <string name="support_label">Flattr ça!</string> <string name="enqueue_all_new">Ajouter tous à la liste</string> <string name="download_all">Tous télécharger</string> <string name="skip_episode_label">Passer cet épisode</string> + <string name="activate_auto_download">Activer téléchargement automatique</string> + <string name="deactivate_auto_download">Désactiver téléchargement automatique</string> <!--Download messages and labels--> <string name="download_successful">terminé</string> <string name="download_failed">échoué</string> @@ -114,6 +146,7 @@ <string name="cancel_all_downloads_label">Annuler tous les téléchargements</string> <string name="download_canceled_msg">Téléchargement annulé</string> <string name="download_report_title">Téléchargements terminés</string> + <string name="download_report_content_title">Rapport des téléchargements</string> <string name="download_error_malformed_url">URL incorrecte</string> <string name="download_error_io_error">Erreur d\'E/S</string> <string name="download_error_request_error">Erreur de requête</string> @@ -129,6 +162,9 @@ <string name="download_request_error_dialog_message_prefix">Une erreur s\'est produite durant le téléchargement du fichier :\u0020</string> <string name="authentication_notification_title">Authentification requise</string> <string name="authentication_notification_msg">La ressource que vous avez demandé nécessite un nom d\'utilisateur et un mot de passe</string> + <string name="confirm_mobile_download_dialog_title">Confirmer téléchargement mobile</string> + <string name="confirm_mobile_download_dialog_only_add_to_queue">Rajouter à la liste</string> + <string name="confirm_mobile_download_dialog_enable_temporarily">Permettre temporairement</string> <!--Mediaplayer messages--> <string name="player_error_msg">Erreur !</string> <string name="player_stopped_msg">Pas de lecture en cours</string> @@ -143,6 +179,8 @@ <string name="playbackservice_notification_title">Lecture de podcast en cours</string> <string name="unknown_media_key">AntennaPod - Touche média inconnue : %1$d</string> <!--Queue operations--> + <string name="lock_queue">Bloquer la liste</string> + <string name="unlock_queue">Débloquer la liste</string> <string name="clear_queue_label">Effacer la liste</string> <string name="undo">Annuler</string> <string name="removed_from_queue">Élément retiré</string> @@ -200,10 +238,12 @@ <string name="pref_followQueue_sum">Après la fin d\'un épisode, passer au suivant</string> <string name="pref_auto_delete_sum">Supprimer l\'épisode quand la lecture est finie</string> <string name="pref_auto_delete_title">Supression automatique</string> + <string name="pref_smart_mark_as_played_sum">Marquer les épisodes comme lus même s\'il reste encore moins d\'un certain intervalle de temps</string> + <string name="pref_smart_mark_as_played_title">Marquer comme lu intelligent</string> <string name="playback_pref">Lecture</string> <string name="network_pref">Réseau</string> - <string name="pref_autoUpdateIntervall_title">Intervalle de mise à jour</string> - <string name="pref_autoUpdateIntervall_sum">Indiquer un intervalle de mise à jour automatique des flux, ou le désactiver</string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Désactiver</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Définir intervalle</string> <string name="pref_downloadMediaOnWifiOnly_sum">Ne télécharger les épisodes que par Wi-Fi</string> <string name="pref_followQueue_title">Lecture continue</string> <string name="pref_downloadMediaOnWifiOnly_title">Téléchargement en Wi-Fi</string> @@ -223,6 +263,10 @@ <string name="pref_auto_flattr_sum">Configurer les paiements flattr automatiques</string> <string name="user_interface_label">Interface utilisateur</string> <string name="pref_set_theme_title">Choisir un thème</string> + <string name="pref_nav_drawer_feed_order_title">Définir ordre des abonnements</string> + <string name="pref_nav_drawer_feed_order_sum">Change l\'ordre de vos abonnements</string> + <string name="pref_nav_drawer_feed_counter_title">Définir compteur des abonnements</string> + <string name="pref_nav_drawer_feed_counter_sum">Change l\'information affichée à côté du compteur des abonnements</string> <string name="pref_set_theme_sum">Modifier l\'apparence d\'AntennaPod.</string> <string name="pref_automatic_download_title">Téléchargement automatique</string> <string name="pref_automatic_download_sum">Configurer le téléchargement automatique des épisodes.</string> @@ -246,15 +290,22 @@ <string name="pref_gpodnet_setlogin_information_sum">Modifier les information de connexion pour votre compte gpodder.net</string> <string name="pref_playback_speed_title">Vitesses de lecture</string> <string name="pref_playback_speed_sum">Modifier la liste des vitesses disponibles pour la lecture audio</string> + <string name="pref_fast_forward">Avance rapide</string> + <string name="pref_rewind">Retour en arrière</string> <string name="pref_gpodnet_sethostname_title">Choisir un nom de domaine</string> <string name="pref_gpodnet_sethostname_use_default_host">Utiliser le nom de domaine par défaut</string> <string name="pref_expandNotify_title">Etendre la notification</string> <string name="pref_expandNotify_sum">Toujours étendre les notifications pour montrer les boutons de lecture</string> <string name="pref_persistNotify_title">Boutons de lecture permanents</string> <string name="pref_persistNotify_sum">Garder les notifications et les boutons de lecture sur l\'écran de verouillage quand la lecture est en pause</string> + <string name="pref_showDownloadReport_title">Afficher rapport des téléchargements</string> + <string name="pref_showDownloadReport_sum">Si les téléchargements échouent, générer un rapport des détails des échecs.</string> <string name="pref_expand_notify_unsupport_toast">Les versions d\'Android antérieures à 4.1 ne sont pas compatibles avec les notifications élargies</string> <string name="pref_queueAddToFront_sum">Ajouter de nouveaux épisodes en tête de file</string> <string name="pref_queueAddToFront_title">Mettre au début de la file d\'attente</string> + <string name="pref_smart_mark_as_played_disabled">Désactivé</string> + <string name="pref_image_cache_size_title">Taille de la cache d\'image</string> + <string name="pref_image_cache_size_sum">Taille de l’espace disque pour stocker temporairement les images.</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Activer le paiement flattr automatique</string> <string name="auto_flattr_after_percent">Lancer un paiement flattr pour un épisode dès que %d de l\'épisode a été joué</string> @@ -280,6 +331,7 @@ <string name="opml_import_error_dir_empty">Le répertoire d\'importation est vide.</string> <string name="select_all_label">Tout choisir</string> <string name="deselect_all_label">Ne rien choisir</string> + <string name="select_options_label">Sélectionner...</string> <string name="choose_file_from_filesystem">Depuis le système de fichier local</string> <string name="choose_file_from_external_application">Utiliser une application tierce</string> <string name="opml_export_label">Exportation OPML</string> @@ -294,9 +346,21 @@ <string name="sleep_timer_label">Arrêt automatique</string> <string name="time_left_label">Durée restante :\u0020</string> <string name="time_dialog_invalid_input">Entrée invalide, la durée doit être un nombre entier</string> - <string name="time_unit_seconds">secondes</string> - <string name="time_unit_minutes">minutes</string> - <string name="time_unit_hours">heures</string> + <string name="time_seconds">secondes</string> + <string name="time_minutes">minutes</string> + <string name="time_hours">heures</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 seconde</item> + <item quantity="other">%d secondes</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minute</item> + <item quantity="other">%d minutes</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 heure</item> + <item quantity="other">%d heures</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">CATEGORIES</string> <string name="gpodnet_toplist_header">PODCASTS POPULAIRES</string> @@ -339,6 +403,9 @@ <string name="set_to_default_folder">Choisir le répertoire par défaut</string> <string name="pref_pausePlaybackForFocusLoss_sum">Mettre la lecture en pause au lieu de baisser le volume quand une autre application veut jouer un son</string> <string name="pref_pausePlaybackForFocusLoss_title">Mettre en pause lors des interruptions</string> + <string name="pref_resumeAfterCall_sum">Reprendre la lecture après un appel téléphonique</string> + <string name="pref_resumeAfterCall_title">Reprendre après appel</string> + <string name="pref_restart_required">AntennaPod doit être redémarré afin que ce changement prenne effet</string> <!--Online feed view--> <string name="subscribe_label">S\'abonner</string> <string name="subscribed_label">Abonné</string> @@ -365,7 +432,29 @@ <!--Feed information screen--> <string name="authentication_label">Authentification</string> <string name="authentication_descr">Modifier votre identifiant et mot de passe pour ce podcast et tous ses épisodes</string> + <!--Progress information--> + <string name="progress_upgrading_database">Mise à jour de la base de données</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importation des abonnements à partir d\'applications à usage unique...</string> <string name="search_itunes_label">Chercher sur iTunes</string> + <string name="select_label"><b>Sélectionner ...</b></string> + <string name="all_label">Tout</string> + <string name="selected_all_label">Sélectionné tous les épisodes</string> + <string name="none_label">Aucun</string> + <string name="deselected_all_label">Désélectionné tous les épisodes</string> + <string name="played_label">Joués</string> + <string name="selected_played_label">Sélectionné tous les épisodes joués</string> + <string name="unplayed_label">Non joués</string> + <string name="selected_unplayed_label">Sélectionné tous les épisodes non joués</string> + <string name="downloaded_label">Téléchargés</string> + <string name="selected_downloaded_label">Sélectionné tous les épisodes téléchargés</string> + <string name="not_downloaded_label">Non téléchargés</string> + <string name="selected_not_downloaded_label">Sélectionné tous les épisodes non téléchargés</string> + <string name="sort_title"><b>Trier par ...</b></string> + <string name="sort_title_a_z">Titre (A \u2192 Z)</string> + <string name="sort_title_z_a">Titre (Z \u2192 A)</string> + <string name="sort_date_new_old">Date (Nouveau \u2192 Ancien)</string> + <string name="sort_date_old_new">Date (Ancien \u2192 Nouveau)</string> + <string name="sort_duration_short_long">Durée (Courte \u2192 Longue)</string> + <string name="sort_duration_long_short">Durée (Longue \u2192 Courte)</string> </resources> diff --git a/core/src/main/res/values-hi-rIN/strings.xml b/core/src/main/res/values-hi-rIN/strings.xml index f32c7c02f..13ef6d489 100644 --- a/core/src/main/res/values-hi-rIN/strings.xml +++ b/core/src/main/res/values-hi-rIN/strings.xml @@ -57,7 +57,6 @@ <string name="remove_feed_label">पॉडकास्ट हटाएँ </string> <string name="share_link_label">शेयर वेबसाइट लिंक</string> - <string name="share_source_label">शेयर फ़ीड लिंक</string> <string name="feed_delete_confirmation_msg">इसकी पुष्टि करें कि आप इस फ़ीड और इस फ़ीड के सभी प्रकरणों को हटाना चाहते हैं जिन्हें आपने डाउनलोड किया है.</string> <string name="feed_remover_msg">फ़ीड निकाल रहा है</string> <!--actions on feeditems--> @@ -158,8 +157,6 @@ <string name="pref_followQueue_sum">प्लेबैक के पूरा होने पर अगली पंक्ति आइटम के लिए जाएँ</string> <string name="playback_pref">प्लेबैक</string> <string name="network_pref">संजाल</string> - <string name="pref_autoUpdateIntervall_title">अंतराल अद्यतन</string> - <string name="pref_autoUpdateIntervall_sum">फ़ीड स्वचालित रूप से ताजा कर रहे हैं जिसमें एक अंतराल निर्दिष्ट करें या उसे निष्क्रिय करें </string> <string name="pref_downloadMediaOnWifiOnly_sum">केवल वाईफ़ाई पर मीडिया फ़ाइलें डाउनलोड करें</string> <string name="pref_followQueue_title">सतत प्लेबैक</string> <string name="pref_downloadMediaOnWifiOnly_title">वाईफाई मीडिया डाउनलोड करें</string> @@ -274,5 +271,6 @@ <string name="downloading_label">डाउनलोड कर रहा है ...</string> <!--Content descriptions for image buttons--> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> </resources> diff --git a/core/src/main/res/values-it-rIT/strings.xml b/core/src/main/res/values-it-rIT/strings.xml index b81e3f2ce..a7ddf81d3 100644 --- a/core/src/main/res/values-it-rIT/strings.xml +++ b/core/src/main/res/values-it-rIT/strings.xml @@ -26,6 +26,11 @@ <!--Main activity--> <string name="drawer_open">Apri il menù</string> <string name="drawer_close">Chiudi il menù</string> + <string name="drawer_feed_order_alphabetical">Ordina alfabeticamente</string> + <string name="drawer_feed_counter_new_unplayed">Numero di episodi nuovi e non riprodotti</string> + <string name="drawer_feed_counter_new">Numero di episodi nuovi</string> + <string name="drawer_feed_counter_unplayed">Numero di episodi non riprodotti</string> + <string name="drawer_feed_counter_none">Nessuno</string> <!--Webview actions--> <string name="open_in_browser_label">Apri nel browser</string> <string name="copy_url_label">Copia URL</string> @@ -37,6 +42,8 @@ <!--Other--> <string name="confirm_label">Conferma</string> <string name="cancel_label">Annulla</string> + <string name="yes">Sì</string> + <string name="no">No</string> <string name="author_label">Autore</string> <string name="language_label">Lingua</string> <string name="url_label">URL</string> @@ -59,7 +66,11 @@ <string name="close_label">Chiudi</string> <string name="retry_label">Riprova</string> <string name="auto_download_label">Includi nei download automatici</string> + <string name="auto_download_apply_to_items_title">Applica ai Precedenti Episodi</string> <string name="parallel_downloads_suffix">\u0020download paralleli</string> + <string name="feed_auto_download_global">Globale</string> + <string name="feed_auto_download_always">Sempre</string> + <string name="feed_auto_download_never">Mai</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL del feed</string> <string name="etxtFeedurlHint">www.example.com/feed</string> @@ -70,10 +81,13 @@ <!--Actions on feeds--> <string name="mark_all_read_label">Segna tutti come riprodotti</string> <string name="mark_all_read_msg">Segnati tutti gli episodi come riprodotti</string> + <string name="mark_all_seen_label">Segna tutti come visti</string> <string name="show_info_label">Informazioni</string> <string name="remove_feed_label">Rimuovi un podcast</string> + <string name="share_label">Condividi...</string> <string name="share_link_label">Condividi il link al sito</string> - <string name="share_source_label">Condividi il link al feed</string> + <string name="share_feed_url_label">Condividi URL del Feed</string> + <string name="share_item_url_label">Condividi URL dell\'Episodio</string> <string name="feed_delete_confirmation_msg">Per favore conferma la cancellazione di questo feed e di TUTTI gli episodi collegati che sono stati precedentemente scaricati.</string> <string name="feed_remover_msg">Rimozione feed</string> <string name="load_complete_feed">Ricarica il feed completo</string> @@ -96,8 +110,8 @@ <string name="remove_label">Rimuovi</string> <string name="remove_episode_lable">Rimuovi l\'episodio</string> <string name="mark_read_label">Segna come riprodotto</string> - <string name="mark_unread_label">Segna come non riprodotto</string> <string name="marked_as_read_label">Segnato come riprodotto</string> + <string name="mark_unread_label">Segna come non riprodotto</string> <string name="add_to_queue_label">Aggiungi alla coda</string> <string name="added_to_queue_label">Aggiunto alla coda</string> <string name="remove_from_queue_label">Rimuovi dalla coda</string> @@ -126,6 +140,7 @@ <string name="download_error_unauthorized">Errore di autenticazione</string> <string name="cancel_all_downloads_label">Annulla tutti i download</string> <string name="download_canceled_msg">Download annullato</string> + <string name="download_report_title">Download completato con un errore (o errori)</string> <string name="download_report_content_title">Rapporto del downoad</string> <string name="download_error_malformed_url">URL malformato</string> <string name="download_error_io_error">Errore IO</string> @@ -219,8 +234,7 @@ <string name="pref_auto_delete_title">Elimina automaticamente</string> <string name="playback_pref">Riproduzione</string> <string name="network_pref">Rete</string> - <string name="pref_autoUpdateIntervall_title">Intervallo di update</string> - <string name="pref_autoUpdateIntervall_sum">Specifica un intervallo per l\'aggiornamento automatico dei feed o disabilitalo</string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Disabilita</string> <string name="pref_downloadMediaOnWifiOnly_sum">Abilita il download dei media solo tramite WiFi</string> <string name="pref_followQueue_title">Playback continuo</string> <string name="pref_downloadMediaOnWifiOnly_title">Download dei media su WiFi</string> @@ -269,6 +283,7 @@ <string name="pref_expandNotify_sum">Espandi sempre le notifiche per mostrare i pulsanti di riproduzione.</string> <string name="pref_persistNotify_title">Controlli di riproduzione persistenti</string> <string name="pref_persistNotify_sum">Mantieni le notifiche e i controlli del blocco dello schermo quando la riproduzione è in pausa.</string> + <string name="pref_showDownloadReport_title">Mostra il Rapporto del Download</string> <string name="pref_expand_notify_unsupport_toast">Le versioni di Android prima della 4.1 non supportano le notifiche estese.</string> <string name="pref_smart_mark_as_played_disabled">Disabilitato</string> <!--Auto-Flattr dialog--> @@ -307,9 +322,21 @@ <string name="sleep_timer_label">Timer di spegnimento</string> <string name="time_left_label">Tempo residuo:\u0020</string> <string name="time_dialog_invalid_input">Input non valido, il campo deve essere un numero intero.</string> - <string name="time_unit_seconds">secondi</string> - <string name="time_unit_minutes">minuti</string> - <string name="time_unit_hours">ore</string> + <string name="time_seconds">secondi</string> + <string name="time_minutes">minuti</string> + <string name="time_hours">ore</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 secondo</item> + <item quantity="other">%d secondi</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minuto</item> + <item quantity="other">%d minuti</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 ora</item> + <item quantity="other">%d ore</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">CATEGORIE</string> <string name="gpodnet_toplist_header">TOP PODCAST</string> @@ -379,7 +406,21 @@ <!--Feed information screen--> <string name="authentication_label">Autenticazione</string> <string name="authentication_descr">Cambia il tuo nome utente e la tua password per questo podcast e i suoi episodi.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importazione di sottoscrizioni da applicazioni monouso in corso...</string> <string name="search_itunes_label">Cerca su iTunes</string> + <string name="select_label"><b>Seleziona ...</b></string> + <string name="all_label">Tutti</string> + <string name="selected_all_label">Tutti gli Episodi Selezionati</string> + <string name="none_label">Nessuno</string> + <string name="deselected_all_label">Tutti gli Episodi Deselezionati</string> + <string name="played_label">Riprodotto</string> + <string name="unplayed_label">Non riprodotto</string> + <string name="sort_title_a_z">Titolo (A \u2192 Z)</string> + <string name="sort_title_z_a">Titolo (Z \u2192 A)</string> + <string name="sort_date_new_old">Data (New \u2192 Old)</string> + <string name="sort_date_old_new">Data (Old \u2192 New)</string> + <string name="sort_duration_short_long">Durata (Short \u2192 Long)</string> + <string name="sort_duration_long_short">Durata (Long \u2192 Short)</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 9e9c0e6bc..07f3602ff 100644 --- a/core/src/main/res/values-iw-rIL/strings.xml +++ b/core/src/main/res/values-iw-rIL/strings.xml @@ -58,7 +58,11 @@ <string name="close_label">סגור</string> <string name="retry_label">נסה שוב</string> <string name="auto_download_label">כלול בהורדות אוטומטיות</string> + <string name="auto_delete_label">מחק לאחר ההשמעה\n(גובר על ההגדרה הגלובלית)</string> <string name="parallel_downloads_suffix">\u0020הורדות במקביל</string> + <string name="feed_auto_download_global">לפי הגדרה גלובלית</string> + <string name="feed_auto_download_always">תמיד</string> + <string name="feed_auto_download_never">אף פעם</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">כתובת הזנה</string> <string name="etxtFeedurlHint">כתובת של הזנה או אתר אינטרנט</string> @@ -74,7 +78,6 @@ <string name="show_info_label">הצג מידע</string> <string name="remove_feed_label">הסר פודקאסט</string> <string name="share_link_label">שתף קישור אתר</string> - <string name="share_source_label">שתף קישור הזנה</string> <string name="feed_delete_confirmation_msg">אשר מחיקת הזנה זו ואת כל פרקי ההזנה שהורדת.</string> <string name="feed_remover_msg">הסר הזנה</string> <string name="load_complete_feed">רענן את כל ההזנה</string> @@ -87,8 +90,8 @@ <string name="remove_label">הסר</string> <string name="remove_episode_lable">הסר פרק</string> <string name="mark_read_label">סמן כנקרא</string> - <string name="mark_unread_label">סמן כלא נקרא</string> <string name="marked_as_read_label">סומן כנקרא</string> + <string name="mark_unread_label">סמן כלא נקרא</string> <string name="add_to_queue_label">הוסף לתור</string> <string name="remove_from_queue_label">הסר מהתור</string> <string name="visit_website_label">בקר באתר</string> @@ -203,8 +206,6 @@ <string name="pref_auto_delete_title">מחיקה אוטומטית</string> <string name="playback_pref">ניגון</string> <string name="network_pref">רשת</string> - <string name="pref_autoUpdateIntervall_title">זמן בין עידכונים</string> - <string name="pref_autoUpdateIntervall_sum">ציין פרק זמן שבו ההזנות עוברות רענון באופן אוטומטי או לבטל ריענון</string> <string name="pref_downloadMediaOnWifiOnly_sum">הורד קבצי מדיה רק דרך חיבור אינטרנט אלחוטי</string> <string name="pref_followQueue_title">ניגון מתמשך</string> <string name="pref_downloadMediaOnWifiOnly_title">הורדת מדיה דרך אינטרנט אלחוטי</string> @@ -247,8 +248,6 @@ <string name="pref_gpodnet_setlogin_information_sum">שנה פרטי התחברות של חשבון gpodder.net.</string> <string name="pref_playback_speed_title">מהירויות ניגון</string> <string name="pref_playback_speed_sum">התאמת המהיריות הזמינות לניגון במהירות משתנה</string> - <string name="pref_seek_delta_title">זמן דילוג</string> - <string name="pref_seek_delta_sum">דלג מספר שניות זה בדילוג לאחור או קדימה</string> <string name="pref_gpodnet_sethostname_title">הגדר שם שרת</string> <string name="pref_gpodnet_sethostname_use_default_host">השתמש בשרת ברירת מידל</string> <string name="pref_expandNotify_title">הרחב הודעה</string> @@ -297,9 +296,6 @@ <string name="sleep_timer_label">טיימר שינה</string> <string name="time_left_label">זמן נותר:\u0020</string> <string name="time_dialog_invalid_input">קלט לא חוקי, זמן חייב להיות מספר שלם</string> - <string name="time_unit_seconds">שניות</string> - <string name="time_unit_minutes">דקות</string> - <string name="time_unit_hours">שעות</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">קטגוריות</string> <string name="gpodnet_toplist_header">פודקאסטים בכירים</string> @@ -368,6 +364,7 @@ <!--Feed information screen--> <string name="authentication_label">אימות</string> <string name="authentication_descr">שנה את שם המשתמש והסיסמה שלך לפודקאסט ופרקים שלו.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">מייבא רישום מאפליקציות יעודיות...</string> <string name="search_itunes_label">חפש בiTunes</string> diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml index 79411ffc1..3a9e58d9a 100644 --- a/core/src/main/res/values-ja/strings.xml +++ b/core/src/main/res/values-ja/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">メニューを開く</string> <string name="drawer_close">メニューを閉じる</string> <string name="drawer_preferences">ドロワー設定</string> + <string name="drawer_feed_order_unplayed_episodes">カウンターで並び替え</string> + <string name="drawer_feed_order_alphabetical">アルファベット順に並び替え</string> + <string name="drawer_feed_counter_new_unplayed">新しい未再生のエピソードの数</string> + <string name="drawer_feed_counter_new">新しいエピソードの数</string> + <string name="drawer_feed_counter_unplayed">未再生のエピソードの数</string> + <string name="drawer_feed_counter_none">なし</string> <!--Webview actions--> <string name="open_in_browser_label">ブラウザーで開く</string> <string name="copy_url_label">URLをコピー</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label">確認</string> <string name="cancel_label">キャンセル</string> + <string name="yes">はい</string> + <string name="no">いいえ</string> <string name="author_label">作者</string> <string name="language_label">言語</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">閉じる</string> <string name="retry_label">再試行</string> <string name="auto_download_label">自動ダウンロードに含む</string> + <string name="auto_download_apply_to_items_title">前のエピソードに適用</string> + <string name="auto_download_apply_to_items_message">新しい <i>自動ダウンロード</i> の設定は、新しいエピソードに自動的に適用されます。\n前のエピソードにも適用しますか?</string> + <string name="auto_delete_label">エピソードの自動削除\n(全般のデフォルトを上書きします)</string> <string name="parallel_downloads_suffix">\u0020パラレル ダウンロード</string> + <string name="feed_auto_download_global">全般</string> + <string name="feed_auto_download_always">常に</string> + <string name="feed_auto_download_never">しない</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">フィードURL</string> <string name="etxtFeedurlHint">フィードまたはWebサイトのURL</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">すべてのエピソードを再生済にしました</string> <string name="mark_all_read_confirmation_msg">再生済としてマークするすべてのエピソードを確認してください。</string> <string name="mark_all_read_feed_confirmation_msg">再生済としてマークするこのフィードのすべてのエピソードを確認してください。</string> + <string name="mark_all_seen_label">参照済としてマーク</string> <string name="show_info_label">情報を表示</string> <string name="remove_feed_label">ポッドキャストを削除</string> + <string name="share_label">共有...</string> <string name="share_link_label">Webサイトのリンクを共有</string> - <string name="share_source_label">フィードのリンクを共有</string> + <string name="share_link_with_position_label">場所とリンクを共有</string> + <string name="share_feed_url_label">フィード URLを共有</string> + <string name="share_item_url_label">エピソード URLを共有</string> + <string name="share_item_url_with_position_label">場所とエピソード URL を共有</string> <string name="feed_delete_confirmation_msg">このフィードと、このフィードのダウンロードしたすべてのエピソードを削除することを確認してください。</string> <string name="feed_remover_msg">フィードの削除中</string> <string name="load_complete_feed">フィードをすべて更新</string> <string name="hide_episodes_title">エピソードを非表示にする</string> + <string name="episode_actions">操作を適用</string> <string name="hide_unplayed_episodes_label">未再生</string> <string name="hide_paused_episodes_label">一時停止しました</string> <string name="hide_played_episodes_label">再生しました</string> @@ -99,8 +119,8 @@ <string name="remove_label">削除</string> <string name="remove_episode_lable">エピソードを削除</string> <string name="mark_read_label">再生済としてマーク</string> - <string name="mark_unread_label">未再生としてマーク</string> <string name="marked_as_read_label">再生済としてマークしました</string> + <string name="mark_unread_label">未再生としてマーク</string> <string name="add_to_queue_label">キューに追加</string> <string name="added_to_queue_label">キューに追加しました</string> <string name="remove_from_queue_label">キューから削除</string> @@ -229,8 +249,12 @@ <string name="pref_smart_mark_as_played_title">再生済としてスマートマーク</string> <string name="playback_pref">再生</string> <string name="network_pref">ネットワーク</string> - <string name="pref_autoUpdateIntervall_title">更新間隔</string> - <string name="pref_autoUpdateIntervall_sum">フィードが自動的に更新される間隔を指定するか、または無効にしてください</string> + <string name="pref_autoUpdateIntervallOrTime_title">間隔または時間を更新</string> + <string name="pref_autoUpdateIntervallOrTime_sum">自動的にフィードを更新する間隔または時間を指定してください</string> + <string name="pref_autoUpdateIntervallOrTime_message">\"2 時間ごと\" のような <i>間隔</i> 、\"7:00 AM\" のような特定の <i>時間</i> 、または自動更新を完全に <i>無効</i> にセットすることができます。\n\n<small>ご注意ください: 更新時間は正確ではありません。少し遅延が発生する可能性があります。</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">無効</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">間隔をセット</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">時間をセット</string> <string name="pref_downloadMediaOnWifiOnly_sum">WiFi接続時のみメディアファイルをダウンロードします</string> <string name="pref_followQueue_title">連続再生</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFiメディアダウンロード</string> @@ -250,8 +274,14 @@ <string name="pref_auto_flattr_sum">自動Flattrを構成</string> <string name="user_interface_label">インターフェース</string> <string name="pref_set_theme_title">テーマを選択</string> + <string name="pref_nav_drawer_title">ナビゲーションドロワーをカスタマイズ</string> + <string name="pref_nav_drawer_sum">ナビゲーションドロワーの外観をカスタマイズします。</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_set_theme_sum">AntennaPodの外観を変更します。</string> <string name="pref_automatic_download_title">自動ダウンロード</string> <string name="pref_automatic_download_sum">エピソードの自動ダウンロードを構成します。</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">常に再生ボタンを表示するように通知を展開します。</string> <string name="pref_persistNotify_title">永続再生コントロール</string> <string name="pref_persistNotify_sum">再生が一時停止された時に、通知およびロック画面のコントロールを保持します。</string> + <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_queueAddToFront_sum">新しいエピソードをキューの先頭に追加します。</string> <string name="pref_queueAddToFront_title">キューの先頭に入れる</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> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">自動Flattrを有効にする</string> <string name="auto_flattr_after_percent">%d %再生したらエピソードをFlattr </string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">インポートディレクトリが空です。</string> <string name="select_all_label">すべてを選択</string> <string name="deselect_all_label">選択解除</string> + <string name="select_options_label">選択 ...</string> <string name="choose_file_from_filesystem">ローカル ファイルシステムから</string> <string name="choose_file_from_external_application">外部アプリケーションを使用する</string> <string name="opml_export_label">OPMLエクスポート</string> @@ -326,9 +361,18 @@ <string name="sleep_timer_label">スリープタイマー</string> <string name="time_left_label">残り時間:\u0020</string> <string name="time_dialog_invalid_input">入力が正しくありません、時間は数字で入力してください</string> - <string name="time_unit_seconds">秒</string> - <string name="time_unit_minutes">分</string> - <string name="time_unit_hours">時</string> + <string name="time_seconds">秒</string> + <string name="time_minutes">分</string> + <string name="time_hours">時間</string> + <plurals name="time_seconds_quantified"> + <item quantity="other">%d 秒</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="other">%d 分</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="other">%d 時間</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">カテゴリー</string> <string name="gpodnet_toplist_header">トップ ボッドキャスト</string> @@ -373,6 +417,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">割り込み時に一時停止</string> <string name="pref_resumeAfterCall_sum">着信が完了した後に再生を再開します</string> <string name="pref_resumeAfterCall_title">着信後に再開</string> + <string name="pref_restart_required">この変更を有効にするには AntennaPod を再起動する必要があります。</string> <!--Online feed view--> <string name="subscribe_label">購読</string> <string name="subscribed_label">購読しました</string> @@ -399,7 +444,29 @@ <!--Feed information screen--> <string name="authentication_label">認証</string> <string name="authentication_descr">このポッドキャストとそのエピソード用のあなたのユーザー名とパスワードを変更します。</string> + <!--Progress information--> + <string name="progress_upgrading_database">データベースをアップグレードしています</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">単一目的のアプリから購読をインポート中…</string> <string name="search_itunes_label">iTunes を検索</string> + <string name="select_label"><b>選択 ...</b></string> + <string name="all_label">すべて</string> + <string name="selected_all_label">すべてのエピソードを選択しました</string> + <string name="none_label">なし</string> + <string name="deselected_all_label">すべてのエピソードの選択を解除しました</string> + <string name="played_label">再生しました</string> + <string name="selected_played_label">再生済のエピソードを選択しました</string> + <string name="unplayed_label">未再生</string> + <string name="selected_unplayed_label">未再生のエピソードを選択しました</string> + <string name="downloaded_label">ダウンロードしました</string> + <string name="selected_downloaded_label">ダウンロード済のエピソードを選択しました</string> + <string name="not_downloaded_label">ダウンロードしていません</string> + <string name="selected_not_downloaded_label">ダウンロードしていないエピソードを選択しました</string> + <string name="sort_title"><b>並び替え順 ...</b></string> + <string name="sort_title_a_z">タイトル (A \u2192 Z)</string> + <string name="sort_title_z_a">タイトル (Z \u2192 A)</string> + <string name="sort_date_new_old">日付 (新 \u2192 旧)</string> + <string name="sort_date_old_new">日付 (旧 \u2192 新)</string> + <string name="sort_duration_short_long">期間 (短 \u2192 長)</string> + <string name="sort_duration_long_short">期間 (長 \u2192 短)</string> </resources> diff --git a/core/src/main/res/values-ko/strings.xml b/core/src/main/res/values-ko/strings.xml index 148010050..7c9f76fcf 100644 --- a/core/src/main/res/values-ko/strings.xml +++ b/core/src/main/res/values-ko/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">메뉴 열기</string> <string name="drawer_close">메뉴 닫기</string> <string name="drawer_preferences">드로어 기본 설정</string> + <string name="drawer_feed_order_unplayed_episodes">카운터로 정렬</string> + <string name="drawer_feed_order_alphabetical">사전 순서로 정렬</string> + <string name="drawer_feed_counter_new_unplayed">새로운 에피소드와 재생하지 않은 에피소드 수</string> + <string name="drawer_feed_counter_new">새로운 에피소드 수</string> + <string name="drawer_feed_counter_unplayed">재생하지 않은 에피소드 수</string> + <string name="drawer_feed_counter_none">없음</string> <!--Webview actions--> <string name="open_in_browser_label">브라우저에서 열기</string> <string name="copy_url_label">URL 복사</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label">확인</string> <string name="cancel_label">취소</string> + <string name="yes">예</string> + <string name="no">아니요</string> <string name="author_label">저자</string> <string name="language_label">언어</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">닫기</string> <string name="retry_label">다시 시도</string> <string name="auto_download_label">자동 다운로드에 포함</string> + <string name="auto_download_apply_to_items_title">예전 에피소드에 적용</string> + <string name="auto_download_apply_to_items_message">새로운 <i>자동 다운로드</I> 설정은 자동으로 새로운 에피소드에 적용됩니다. 예전 에피소드에도 적용하시겠습니까?</string> + <string name="auto_delete_label">에피소드 자동 삭제\n(전체 설정보다 우선)</string> <string name="parallel_downloads_suffix">\u0020동시 다운로드</string> + <string name="feed_auto_download_global">전체 설정</string> + <string name="feed_auto_download_always">항상</string> + <string name="feed_auto_download_never">안 함</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">피드 URL</string> <string name="etxtFeedurlHint">피드의 URL 또는 홈페이지</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">모든 에피소드를 재생했다고 표시했습니다</string> <string name="mark_all_read_confirmation_msg">모든 에피소드를 재생했다고 표시할지 확인하십시오.</string> <string name="mark_all_read_feed_confirmation_msg">이 피드에 들어 있는 모든 에피소드를 재생했다고 표시할지 확인하십시오.</string> + <string name="mark_all_seen_label">모두 봤다고 표시</string> <string name="show_info_label">정보 표시</string> <string name="remove_feed_label">팟캐스트 제거</string> + <string name="share_label">공유...</string> <string name="share_link_label">홈페이지 링크 공유</string> - <string name="share_source_label">피드 링크 공유</string> + <string name="share_link_with_position_label">위치와 같이 링크 공유</string> + <string name="share_feed_url_label">피드 URL 공유</string> + <string name="share_item_url_label">에피소드 URL 공유</string> + <string name="share_item_url_with_position_label">위치와 같이 에피소드 URL 공유</string> <string name="feed_delete_confirmation_msg">이 피드와 이 피드에서 다운로드한 모든 에피소드를 삭제하시려면 확인을 누르십시오.</string> <string name="feed_remover_msg">피드 삭제하는 중</string> <string name="load_complete_feed">전체 피드 새로고침</string> <string name="hide_episodes_title">에피소드 감추기</string> + <string name="episode_actions">동작 적용</string> <string name="hide_unplayed_episodes_label">재생 안 함</string> <string name="hide_paused_episodes_label">일시 중지</string> <string name="hide_played_episodes_label">재생함</string> @@ -99,8 +119,8 @@ <string name="remove_label">제거</string> <string name="remove_episode_lable">에피소드 제거</string> <string name="mark_read_label">재생했다고 표시</string> - <string name="mark_unread_label">재생하지 않음으로 표시</string> <string name="marked_as_read_label">재생했다고 표시함</string> + <string name="mark_unread_label">재생하지 않음으로 표시</string> <string name="add_to_queue_label">대기열에 추가</string> <string name="added_to_queue_label">대기열에 추가함</string> <string name="remove_from_queue_label">대기열에서 제거</string> @@ -229,8 +249,12 @@ <string name="pref_smart_mark_as_played_title">똑똑하게 재생한 것으로 표시</string> <string name="playback_pref">재생</string> <string name="network_pref">네트워크</string> - <string name="pref_autoUpdateIntervall_title">업데이트 주기</string> - <string name="pref_autoUpdateIntervall_sum">피드를 새로 고칠 주기를 지정하거나 새로 고침을 하지 않음</string> + <string name="pref_autoUpdateIntervallOrTime_title">업데이트 주기 또는 하루 중 시각</string> + <string name="pref_autoUpdateIntervallOrTime_sum">피드를 자동으로 새로 고칠 주기 또는 하루 중 특정 시각을 지정하십시오</string> + <string name="pref_autoUpdateIntervallOrTime_message">\"매 2시간\"과 같이 <i>주기</i>를 지정할 수도 있고, \"오전 7:00\"와 같이 <i>하루 중 시각</i>을 지정할 수도 있고, 자동 업데이트를 모두 <i>사용 안 할</i> 수도 있습니다.\n\n<small>안내: 업데이트 시간은 정확하지 않습니다. 약간 늦게 업데이트할 수 있습니다.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">사용 안 함</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">주기 지정</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">하루 중 시각 지정</string> <string name="pref_downloadMediaOnWifiOnly_sum">Wi-Fi를 통해서만 미디어 파일 다운로드</string> <string name="pref_followQueue_title">연속 재생</string> <string name="pref_downloadMediaOnWifiOnly_title">Wi-Fi 미디어 다운로드</string> @@ -250,8 +274,14 @@ <string name="pref_auto_flattr_sum">자동 flattr 설정</string> <string name="user_interface_label">사용자 인터페이스</string> <string name="pref_set_theme_title">테마 선택</string> + <string name="pref_nav_drawer_title">네비게이션 드로어 사용자 설정</string> + <string name="pref_nav_drawer_sum">네비게이션 드로어의 모양을 사용자 설정합니다.</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_set_theme_sum">안테나팟의 겉모양을 바꿉니다.</string> <string name="pref_automatic_download_title">자동 다운로드</string> <string name="pref_automatic_download_sum">에피소드 자동 다운로드를 설정합니다.</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">항상 알림에서 재생 버튼이 표시되도록 확장</string> <string name="pref_persistNotify_title">재생 조작 고정</string> <string name="pref_persistNotify_sum">재생이 일시 중지했을 때에도 알림과 잠금 화면의 조작 기능 유지</string> + <string name="pref_showDownloadReport_title">다운로드 보고서 보기</string> + <string name="pref_showDownloadReport_sum">다운로드가 실패하면, 실패를 자세히 표시하는 보고서를 만듭니다.</string> <string name="pref_expand_notify_unsupport_toast">안드로이드 4.1 전 버전에서는 알림 확장을 지원하지 않습니다.</string> <string name="pref_queueAddToFront_sum">새 에피소드를 대기열 앞에 추가합니다.</string> <string name="pref_queueAddToFront_title">대기열 앞에 추가</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> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">자동 flattr 사용</string> <string name="auto_flattr_after_percent">%d 퍼센트를 재생하면 에피소드에 flattr합니다</string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">가져오기 디렉터리가 비어 있습니다.</string> <string name="select_all_label">모두 선택</string> <string name="deselect_all_label">모두 선택 해제</string> + <string name="select_options_label">선택...</string> <string name="choose_file_from_filesystem">로컬 파일시스템에서</string> <string name="choose_file_from_external_application">외부 앱 사용</string> <string name="opml_export_label">OPML 내보내기</string> @@ -326,9 +361,18 @@ <string name="sleep_timer_label">취침 타이머</string> <string name="time_left_label">남은 시간:\u0020</string> <string name="time_dialog_invalid_input">입력이 잘못되었습니다. 시간으로 숫자를 입력해야 합니다.</string> - <string name="time_unit_seconds">초</string> - <string name="time_unit_minutes">분</string> - <string name="time_unit_hours">시간</string> + <string name="time_seconds">초</string> + <string name="time_minutes">분</string> + <string name="time_hours">시간</string> + <plurals name="time_seconds_quantified"> + <item quantity="other">%d초</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="other">%d분</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="other">%d시간</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">분류</string> <string name="gpodnet_toplist_header">상위 팟캐스트</string> @@ -373,6 +417,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">끼어들면 일시 중지</string> <string name="pref_resumeAfterCall_sum">전화 통화가 끝난 후에 재생 다시 시작</string> <string name="pref_resumeAfterCall_title">통화 후에 다시 시작</string> + <string name="pref_restart_required">이 변경 사항을 적용하려면 안테나팟을 다시 시작해야 합니다.</string> <!--Online feed view--> <string name="subscribe_label">구독</string> <string name="subscribed_label">구독함</string> @@ -399,7 +444,29 @@ <!--Feed information screen--> <string name="authentication_label">인증</string> <string name="authentication_descr">이 팟캐스트와 에피소드에 대한 사용자 이름과 비밀번호를 바꿉니다.</string> + <!--Progress information--> + <string name="progress_upgrading_database">데이터베이스 업그레이드 중</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">단일 용도 앱에서 구독 정보를 가져옵니다...</string> <string name="search_itunes_label">iTunes 검색</string> + <string name="select_label"><b>선택 ...</b></string> + <string name="all_label">모두</string> + <string name="selected_all_label">모든 에피소드 선택</string> + <string name="none_label">없음</string> + <string name="deselected_all_label">모든 에피소드 선택 해제</string> + <string name="played_label">재생함</string> + <string name="selected_played_label">재생 에피소드 선택</string> + <string name="unplayed_label">재생 안 함</string> + <string name="selected_unplayed_label">재생 안 한 에피소드 선택</string> + <string name="downloaded_label">다운로드함</string> + <string name="selected_downloaded_label">다운로드한 에피소드 선택</string> + <string name="not_downloaded_label">다운로드 안 함</string> + <string name="selected_not_downloaded_label">다운로드 안 한 에피소드 선택</string> + <string name="sort_title"><b>정렬 ...</b></string> + <string name="sort_title_a_z">제목 (A \u2192 Z)</string> + <string name="sort_title_z_a">제목 (Z \u2192 A)</string> + <string name="sort_date_new_old">시각 (최근 \u2192 과거)</string> + <string name="sort_date_old_new">시각 (과거 \u2192 최근)</string> + <string name="sort_duration_short_long">길이 (짧은 \u2192 긴)</string> + <string name="sort_duration_long_short">길이 (긴 \u2192 짧은)</string> </resources> diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml index 0f447d54a..4ef43ecaf 100644 --- a/core/src/main/res/values-nl/strings.xml +++ b/core/src/main/res/values-nl/strings.xml @@ -54,7 +54,6 @@ <string name="mark_all_read_label">Alles als gelezen markeren</string> <string name="show_info_label">Toon informatie</string> <string name="share_link_label">Website link delen</string> - <string name="share_source_label">Feed link delen</string> <string name="feed_delete_confirmation_msg">Bevestig dat u deze feed en ALLE afleveringen van deze feed die u hebt gedownload wilt verwijderen.</string> <string name="feed_remover_msg">Feed verwijderen</string> <!--actions on feeditems--> @@ -162,8 +161,6 @@ <string name="pref_followQueue_sum">Volgende wachtrij item afspelen als de episode voltooid is</string> <string name="playback_pref">Afspelen</string> <string name="network_pref">Netwerk</string> - <string name="pref_autoUpdateIntervall_title">Update interval</string> - <string name="pref_autoUpdateIntervall_sum">Voer een tijdsinterval in waarin de feeds automatisch worden vernieuwd, of schakel het uit</string> <string name="pref_downloadMediaOnWifiOnly_sum">Download mediabestanden alleen via WiFi</string> <string name="pref_followQueue_title">Continu afspelen</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi download van media</string> @@ -295,6 +292,7 @@ <string name="new_episodes_count_label">Aantal nieuwe afleveringen</string> <string name="in_progress_episodes_count_label">Aantal afleveringen dat begonnen te luisteren zijn</string> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Abonnementen aan het importeren vanuit single-purpose apps...</string> </resources> diff --git a/core/src/main/res/values-no/strings.xml b/core/src/main/res/values-no/strings.xml new file mode 100644 index 000000000..645d576a4 --- /dev/null +++ b/core/src/main/res/values-no/strings.xml @@ -0,0 +1,31 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources xmlns:tools="http://schemas.android.com/tools"> + <!--Activitiy and fragment titles--> + <!--New episodes fragment--> + <!--Main activity--> + <!--Webview actions--> + <!--Playback history--> + <!--Other--> + <!--'Add Feed' Activity labels--> + <!--Actions on feeds--> + <!--actions on feeditems--> + <!--Download messages and labels--> + <!--Mediaplayer messages--> + <!--Queue operations--> + <!--Flattr--> + <!--Flattr--> + <!--Variable Speed--> + <!--Empty list labels--> + <!--Preferences--> + <!--Auto-Flattr dialog--> + <!--Search--> + <!--OPML import and export--> + <!--Sleep timer--> + <!--gpodder.net--> + <!--Directory chooser--> + <!--Online feed view--> + <!--Content descriptions for image buttons--> + <!--Feed information screen--> + <!--Progress information--> + <!--AntennaPodSP--> +</resources> diff --git a/core/src/main/res/values-pl-rPL/strings.xml b/core/src/main/res/values-pl-rPL/strings.xml index ba1a0bb91..a43ce7722 100644 --- a/core/src/main/res/values-pl-rPL/strings.xml +++ b/core/src/main/res/values-pl-rPL/strings.xml @@ -71,7 +71,6 @@ <string name="show_info_label">Pokaż informacje</string> <string name="remove_feed_label">Usuń podcast</string> <string name="share_link_label">Udostępnij stronę</string> - <string name="share_source_label">Udostępnij kanał</string> <string name="feed_delete_confirmation_msg">Potwierdź chęć usunięcia tego kanału wraz ze WSZYSTKIMI odcinkami, które zostały pobrane.</string> <string name="feed_remover_msg">Usuwanie kanału</string> <string name="load_complete_feed">Odśwież cały kanał</string> @@ -197,8 +196,6 @@ <string name="pref_auto_delete_title">Automatyczne usuwanie</string> <string name="playback_pref">Odtwarzanie</string> <string name="network_pref">Sieć</string> - <string name="pref_autoUpdateIntervall_title">Częstość aktualizacji</string> - <string name="pref_autoUpdateIntervall_sum">Określ częstotliwość automatycznego odświeżania lub je wyłącz</string> <string name="pref_downloadMediaOnWifiOnly_sum">Pobieraj pliki tylko przez WiFi</string> <string name="pref_followQueue_title">Odtwarzanie ciągłe</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi media pobrane</string> @@ -238,8 +235,6 @@ <string name="pref_gpodnet_setlogin_information_sum">Zmień dane logowania konta gpodder.net.</string> <string name="pref_playback_speed_title">Prędkość odtwarzania</string> <string name="pref_playback_speed_sum">Dostosuj prędkości dostępne dla odtwarzania audio o zmiennej prędkości</string> - <string name="pref_seek_delta_title">Seek time</string> - <string name="pref_seek_delta_sum">Przeskocz o tyle sekund przewijając</string> <string name="pref_gpodnet_sethostname_title">Ustaw nazwę hosta</string> <string name="pref_gpodnet_sethostname_use_default_host">Użyj domyślnego hosta</string> <string name="pref_expandNotify_title">Rozwiń Powiadomienia</string> @@ -281,9 +276,6 @@ <string name="sleep_timer_label">Wyłącznik czasowy</string> <string name="time_left_label">Pozostały czas:\u0020</string> <string name="time_dialog_invalid_input">Błąd wpisu, czas musi być liczbą całkowitą</string> - <string name="time_unit_seconds">sekundy</string> - <string name="time_unit_minutes">minuty</string> - <string name="time_unit_hours">godziny</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORIE</string> <string name="gpodnet_toplist_header">TOP PODCASTY</string> @@ -353,6 +345,7 @@ https://gpodder.net/register/</string> <!--Feed information screen--> <string name="authentication_label">Autoryzacja</string> <string name="authentication_descr">Zmień swoją nazwę użytkownika oraz hasło dla tego podcastu i jego odcinków</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importowanie subskrybcji z jednozadaniowych aplikacji</string> </resources> diff --git a/core/src/main/res/values-pt-rBR/strings.xml b/core/src/main/res/values-pt-rBR/strings.xml index c3523acfb..3d421fdb2 100644 --- a/core/src/main/res/values-pt-rBR/strings.xml +++ b/core/src/main/res/values-pt-rBR/strings.xml @@ -54,7 +54,6 @@ <string name="mark_all_read_label">Marcar todos como lido</string> <string name="show_info_label">Mostrar informação</string> <string name="share_link_label">Compartilhar link do site</string> - <string name="share_source_label">Compartilhar link do feed</string> <string name="feed_delete_confirmation_msg">Por favor confirme que você deseja apagar este feed e TODOS os episódios que você fez download deste feed.</string> <string name="feed_remover_msg">Removendo feed</string> <!--actions on feeditems--> @@ -148,8 +147,6 @@ <string name="pref_followQueue_sum">Pular para próximo item da fila quando a reprodução terminar</string> <string name="playback_pref">Reprodução</string> <string name="network_pref">Rede</string> - <string name="pref_autoUpdateIntervall_title">Intervalo de atualização</string> - <string name="pref_autoUpdateIntervall_sum">Especifica o intervalo com que os feeds serão atualizados automaticamente ou desabilita esta funcionalidade</string> <string name="pref_downloadMediaOnWifiOnly_sum">Fazer download dos arquivos apenas via rede WiFi</string> <string name="pref_followQueue_title">Reprodução contínua</string> <string name="pref_downloadMediaOnWifiOnly_title">Download de mídia via WiFi</string> @@ -271,5 +268,6 @@ <string name="in_queue_label">Episódio está na fila</string> <string name="new_episodes_count_label">Numero de novos episódios</string> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> </resources> diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml index abbf97de6..f02cd629e 100644 --- a/core/src/main/res/values-pt/strings.xml +++ b/core/src/main/res/values-pt/strings.xml @@ -16,7 +16,7 @@ <string name="downloads_running_label">Em curso</string> <string name="downloads_completed_label">Terminadas</string> <string name="downloads_log_label">Registo</string> - <string name="cancel_download_label">Cancelar transferência</string> + <string name="cancel_download_label">Cancelar\ntransferência</string> <string name="playback_history_label">Histórico de reprodução</string> <string name="gpodnet_main_label">gpodder.net</string> <string name="gpodnet_auth_label">Dados gpodder.net</string> @@ -27,6 +27,12 @@ <string name="drawer_open">Abrir menu</string> <string name="drawer_close">Fechar menu</string> <string name="drawer_preferences">Preferências do menu</string> + <string name="drawer_feed_order_unplayed_episodes">Ordenar por contador</string> + <string name="drawer_feed_order_alphabetical">Ordenar alfabeticamente</string> + <string name="drawer_feed_counter_new_unplayed">Número de episódios novos ou por reproduzir</string> + <string name="drawer_feed_counter_new">Número de novos episódios</string> + <string name="drawer_feed_counter_unplayed">Número de episódios por reproduzir</string> + <string name="drawer_feed_counter_none">Nenhum</string> <!--Webview actions--> <string name="open_in_browser_label">Abrir no navegador</string> <string name="copy_url_label">Copiar URL</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label">Confirmar</string> <string name="cancel_label">Cancelar</string> + <string name="yes">Sim</string> + <string name="no">Não</string> <string name="author_label">Autor</string> <string name="language_label">Idioma</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">Fechar</string> <string name="retry_label">Tentar novamente</string> <string name="auto_download_label">Incluir nas transferências automáticas</string> + <string name="auto_download_apply_to_items_title">Aplicar aos episódios anteriores</string> + <string name="auto_download_apply_to_items_message">A definição <i>Transferência automática</i> será aplicada a todos os novos episódios.\nGostaria de também a aplicar aos episódios anteriores?</string> + <string name="auto_delete_label">Apagar episódio automáticamente\n(altera a definição global)</string> <string name="parallel_downloads_suffix">\u0020transferências simultâneas</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Sempre</string> + <string name="feed_auto_download_never">Nunca</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL da fonte</string> <string name="etxtFeedurlHint">URL da fonte ou sítio web</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">Marcar todos os episódios como reproduzidos</string> <string name="mark_all_read_confirmation_msg">Por favor confirme que deseja marcar todos os episódios como reproduzidos</string> <string name="mark_all_read_feed_confirmation_msg">Por favor confirme que deseja marcar todos os episódios desta fonte como reproduzidos</string> + <string name="mark_all_seen_label">Marcar tudo como visto</string> <string name="show_info_label">Mostrar informações</string> <string name="remove_feed_label">Remover podcast</string> - <string name="share_link_label">Partilhar ligação do sítio web</string> - <string name="share_source_label">Partilhar ligação da fonte</string> - <string name="feed_delete_confirmation_msg">Confirme a eliminação desta fonte e de todos os episódios a ela pertencentes</string> + <string name="share_label">Partilhar...</string> + <string name="share_link_label">Partilhar ligação</string> + <string name="share_link_with_position_label">Partilhar ligação com posição</string> + <string name="share_feed_url_label">Partilhar URL da fonte</string> + <string name="share_item_url_label">Partilhar URL do episódio</string> + <string name="share_item_url_with_position_label">Partilhar URL do episódio com posição</string> + <string name="feed_delete_confirmation_msg">Por favor confirme que deseja apagar esta fonte e todos os episódios transferidos</string> <string name="feed_remover_msg">Remover fonte</string> <string name="load_complete_feed">Atualizar todas as páginas da fonte</string> <string name="hide_episodes_title">Ocultar episódios</string> + <string name="episode_actions">Aplicar ações</string> <string name="hide_unplayed_episodes_label">Não reproduzidos</string> <string name="hide_paused_episodes_label">Em pausa</string> <string name="hide_played_episodes_label">Reproduzidos</string> @@ -99,8 +119,8 @@ <string name="remove_label">Remover</string> <string name="remove_episode_lable">Remover episódio</string> <string name="mark_read_label">Marcar como reproduzido</string> - <string name="mark_unread_label">Marcar como não reproduzido</string> <string name="marked_as_read_label">Marcado como reproduzido</string> + <string name="mark_unread_label">Marcar como não reproduzido</string> <string name="add_to_queue_label">Adicionar à fila</string> <string name="added_to_queue_label">Adicionado à fila</string> <string name="remove_from_queue_label">Remover da fila</string> @@ -138,7 +158,7 @@ <string name="download_error_db_access">Erro de acesso à base de dados</string> <string name="downloads_left">\u0020Transferências em falta</string> <string name="downloads_processing">Processamento de transferências</string> - <string name="download_notification_title">A transferir dados...</string> + <string name="download_notification_title">A transferir dados do podcast</string> <string name="download_report_content">%1$d transferências efetuadas, %2$d falhadas</string> <string name="download_log_title_unknown">Título desconhecido</string> <string name="download_type_feed">Fonte</string> @@ -209,7 +229,7 @@ <!--Variable Speed--> <string name="download_plugin_label">Transferir extra</string> <string name="no_playback_plugin_title">Extra não instalado</string> - <string name="no_playback_plugin_msg">Para que a velocidade de reprodução variável funcione, tem que instalar um biblioteca de terceiros.\n\nClique em Transferir extra para a transferir no Google Play.\n\nQuaisquer problemas que ocorram na utilização do extra devem ser reportados diretamente ao seu programador.</string> + <string name="no_playback_plugin_msg">Para que a velocidade variável de reprodução funcione, tem que instalar um biblioteca de terceiros.\n\nClique em Transferir extra para a transferir no Google Play.\n\nQuaisquer erros que ocorram na utilização do extra devem ser reportados diretamente ao seu programador.</string> <string name="set_playback_speed_label">Velocidades de reprodução</string> <!--Empty list labels--> <string name="no_items_label">Não existem itens nesta lista</string> @@ -223,19 +243,23 @@ <string name="pref_pauseOnHeadsetDisconnect_sum">Pausa na reprodução ao remover os auscultadores</string> <string name="pref_unpauseOnHeadsetReconnect_sum">Continuar reprodução ao ligar os auscultadores</string> <string name="pref_followQueue_sum">Ir para a faixa seguinte ao terminar a reprodução</string> - <string name="pref_auto_delete_sum">Eliminar episódio ao terminar a reprodução</string> + <string name="pref_auto_delete_sum">Apagar episódio ao terminar a reprodução</string> <string name="pref_auto_delete_title">Eliminação automática</string> <string name="pref_smart_mark_as_played_sum">Marcar episódios como reproduzidos mesmo que restem alguns segundos de reprodução</string> <string name="pref_smart_mark_as_played_title">Marcar como reproduzido (inteligente)</string> <string name="playback_pref">Reprodução</string> <string name="network_pref">Rede</string> - <string name="pref_autoUpdateIntervall_title">Intervalo entre atualizações</string> - <string name="pref_autoUpdateIntervall_sum">Indique o intervalo de tempo entre as atualizações de fontes ou desative a opção</string> - <string name="pref_downloadMediaOnWifiOnly_sum">Apenas transferir pelas redes sem fios</string> + <string name="pref_autoUpdateIntervallOrTime_title">Intervalo de atualização ou hora do dia</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Define um intervalo de atualização ou hora para atualizar automaticamente a fonte</string> + <string name="pref_autoUpdateIntervallOrTime_message">Pode definir um <i>intervalo</i>, ex.: \"a cada 2 horas\", definir uma <i>hora do dia</i>, ex.: \"7:00 AM\" ou <i>desativar</i> as atualizações automáticas.\n\n<small>Tenha em conta que a hora de atualização não é precisa. Pode existir um pouco de atraso.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Desativar</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Definir intervalo</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Definir hora do dia</string> + <string name="pref_downloadMediaOnWifiOnly_sum">Apenas transferir através de redes sem fios</string> <string name="pref_followQueue_title">Reprodução contínua</string> - <string name="pref_downloadMediaOnWifiOnly_title">Transferência Wi-Fi</string> + <string name="pref_downloadMediaOnWifiOnly_title">Transferir por Wi-Fi</string> <string name="pref_pauseOnHeadsetDisconnect_title">Auscultadores removidos</string> - <string name="pref_unpauseOnHeadsetReconnect_title">Auscultadores ligados</string> + <string name="pref_unpauseOnHeadsetReconnect_title">Auscultadores inseridos</string> <string name="pref_mobileUpdate_title">Atualizações móveis</string> <string name="pref_mobileUpdate_sum">Permitir atualizações através da rede de dados móveis</string> <string name="refreshing_label">A atualizar</string> @@ -250,14 +274,20 @@ <string name="pref_auto_flattr_sum">Configurar flattr automático</string> <string name="user_interface_label">Interface</string> <string name="pref_set_theme_title">Tema</string> - <string name="pref_nav_drawer_items_title">Alterar itens do menu</string> + <string name="pref_nav_drawer_title">Personalizar menu de navegação</string> + <string name="pref_nav_drawer_sum">Personaliza a aparência do menu de navegação</string> + <string name="pref_nav_drawer_items_title">Alterar elementos do menu</string> <string name="pref_nav_drawer_items_sum">Alterar os itens que aparecem no menu de navegação</string> + <string name="pref_nav_drawer_feed_order_title">Definir ordem de subscrição</string> + <string name="pref_nav_drawer_feed_order_sum">Alterar a ordem das suas subscrições</string> + <string name="pref_nav_drawer_feed_counter_title">Definir contador de subsrições</string> + <string name="pref_nav_drawer_feed_counter_sum">Mudar informação mostrada no contador de subscrições</string> <string name="pref_set_theme_sum">Mudar o aspeto do AntennaPod</string> <string name="pref_automatic_download_title">Transferência automática</string> <string name="pref_automatic_download_sum">Configure a transferência automática dos episódios</string> <string name="pref_autodl_wifi_filter_title">Ativar filtro Wi-Fi</string> <string name="pref_autodl_wifi_filter_sum">Apenas permitir transferências automáticas através de redes sem fios</string> - <string name="pref_automatic_download_on_battery_title">Transferência se não estiver a carregar</string> + <string name="pref_automatic_download_on_battery_title">Transferir se não estiver a carregar</string> <string name="pref_automatic_download_on_battery_sum">Permitir transferência automática se a bateria não estiver a ser carregada</string> <string name="pref_parallel_downloads_title">Transferências simultâneas</string> <string name="pref_episode_cache_title">Cache de episódios</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">Expandir sempre a notificação para mostrar os botões de reprodução</string> <string name="pref_persistNotify_title">Controlos de reprodução persistentes</string> <string name="pref_persistNotify_sum">Manter controlos de notificação e ecrã de bloqueio ao colocar a reprodução em pausa</string> + <string name="pref_showDownloadReport_title">Mostrar relatório de erros</string> + <string name="pref_showDownloadReport_sum">Se a transferência falhar, gera um relatório que mostra os detalhes do erro</string> <string name="pref_expand_notify_unsupport_toast">As versões Android anteriores à 4.1 não possuem suporte à expansão de notificações</string> <string name="pref_queueAddToFront_sum">Colocar novos episódios no inicio da fila</string> <string name="pref_queueAddToFront_title">Novos episódios no inicio</string> <string name="pref_smart_mark_as_played_disabled">Desativada</string> + <string name="pref_image_cache_size_title">Cache de imagens</string> + <string name="pref_image_cache_size_sum">O tamanho da cache de imagens</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Ativar flattr automático</string> <string name="auto_flattr_after_percent">Flattr de episódios ao atingir %d porcento de reprodução</string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">O diretório de importação está vazio</string> <string name="select_all_label">Marcar tudo</string> <string name="deselect_all_label">Desmarcar tudo</string> + <string name="select_options_label">Selecionar...</string> <string name="choose_file_from_filesystem">Do sistema local de ficheiros</string> <string name="choose_file_from_external_application">Utilizar aplicação externa</string> <string name="opml_export_label">Exportação OPML</string> @@ -326,9 +361,21 @@ <string name="sleep_timer_label">Temporizador</string> <string name="time_left_label">Tempo restante:\u0020</string> <string name="time_dialog_invalid_input">Tem que introduzir um número inteiro</string> - <string name="time_unit_seconds">segundos</string> - <string name="time_unit_minutes">minutos</string> - <string name="time_unit_hours">horas</string> + <string name="time_seconds">segundos</string> + <string name="time_minutes">minutos</string> + <string name="time_hours">horas</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 segundo</item> + <item quantity="other">%d segundos</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minuto</item> + <item quantity="other">%d minutos</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 hora</item> + <item quantity="other">%d horas</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">Categorias</string> <string name="gpodnet_toplist_header">Melhores</string> @@ -373,6 +420,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">Pausa nas interrupções</string> <string name="pref_resumeAfterCall_sum">Continuar reprodução ao terminar a chamada</string> <string name="pref_resumeAfterCall_title">Continuar após a chamada</string> + <string name="pref_restart_required">Tem que reiniciar o AntennaPod para aplicar as alterações</string> <!--Online feed view--> <string name="subscribe_label">Subscrever</string> <string name="subscribed_label">Subscrito</string> @@ -399,7 +447,29 @@ <!--Feed information screen--> <string name="authentication_label">Autenticação</string> <string name="authentication_descr">Altere o seu nome de utilizador e senha para este podcast e seus episódios</string> + <!--Progress information--> + <string name="progress_upgrading_database">Atualizando base de dados</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importar subscrições de aplicações single-purpose...</string> <string name="search_itunes_label">Procurar no iTunes</string> + <string name="select_label"><b>Selecionar...</b></string> + <string name="all_label">Todos</string> + <string name="selected_all_label">Marcar todos os episódios</string> + <string name="none_label">Nenhum</string> + <string name="deselected_all_label">Desmarcar todos os episódios</string> + <string name="played_label">Reproduzidos</string> + <string name="selected_played_label">Selecionar episódios reproduzidos</string> + <string name="unplayed_label">Não reproduzidos</string> + <string name="selected_unplayed_label">Selecionar episódios não reproduzidos</string> + <string name="downloaded_label">Transferidos</string> + <string name="selected_downloaded_label">Selecionar episódios transferidos</string> + <string name="not_downloaded_label">Não transferidos</string> + <string name="selected_not_downloaded_label">Selecionar episódios não transferidos</string> + <string name="sort_title"><b>Ordenar por...</b></string> + <string name="sort_title_a_z">Título (A \u2192 Z)</string> + <string name="sort_title_z_a">Título (Z \u2192 A)</string> + <string name="sort_date_new_old">Data (Recente \u2192 Antiga)</string> + <string name="sort_date_old_new">Data (Antiga \u2192 Recente)</string> + <string name="sort_duration_short_long">Duração (Curta \u2192 Longa)</string> + <string name="sort_duration_long_short">Duração (Longa \u2192 Curta)</string> </resources> diff --git a/core/src/main/res/values-ro-rRO/strings.xml b/core/src/main/res/values-ro-rRO/strings.xml index 390f50767..ce82dec1e 100644 --- a/core/src/main/res/values-ro-rRO/strings.xml +++ b/core/src/main/res/values-ro-rRO/strings.xml @@ -50,7 +50,6 @@ <string name="mark_all_read_label">Marchează toate ca citite</string> <string name="show_info_label">Arată informații</string> <string name="share_link_label">Împarte adresă website</string> - <string name="share_source_label">Împarte adresă feed</string> <string name="feed_delete_confirmation_msg">Confirmați ștergerea feedului și a TUTUROR episoadelor pe care le-ați descărcat.</string> <!--actions on feeditems--> <string name="download_label">Descarcă</string> @@ -140,8 +139,6 @@ <string name="pref_followQueue_sum">Sari la următorul element din coadă cand se termină ascultarea</string> <string name="playback_pref">Ascultare</string> <string name="network_pref">Rețea</string> - <string name="pref_autoUpdateIntervall_title">Interval actualizare</string> - <string name="pref_autoUpdateIntervall_sum">Specifică un interval în care feedurile sunt actualizate automat sau oprește funcția</string> <string name="pref_downloadMediaOnWifiOnly_sum">Descarcă fișiere media doar pe WiFi</string> <string name="pref_followQueue_title">Ascultare continuă</string> <string name="pref_downloadMediaOnWifiOnly_title">Descărcare media pe WiFi</string> @@ -237,5 +234,6 @@ <string name="downloading_label">Se descarcă...</string> <!--Content descriptions for image buttons--> <!--Feed information screen--> + <!--Progress information--> <!--AntennaPodSP--> </resources> diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml index ae10b314f..c2c27484a 100644 --- a/core/src/main/res/values-ru/strings.xml +++ b/core/src/main/res/values-ru/strings.xml @@ -17,7 +17,7 @@ <string name="downloads_completed_label">Завершено</string> <string name="downloads_log_label">Журнал</string> <string name="cancel_download_label">Отменить загрузку</string> - <string name="playback_history_label">История воспроизведения</string> + <string name="playback_history_label">Журнал</string> <string name="gpodnet_main_label">gpodder.net</string> <string name="gpodnet_auth_label">Войти на gpodder.net</string> <!--New episodes fragment--> @@ -26,6 +26,12 @@ <!--Main activity--> <string name="drawer_open">Открыть меню</string> <string name="drawer_close">Закрыть меню</string> + <string name="drawer_feed_order_unplayed_episodes">Сортировать по количеству</string> + <string name="drawer_feed_order_alphabetical">Сортировать по алфавиту</string> + <string name="drawer_feed_counter_new_unplayed">Количество новых и непрослушанных выпусков</string> + <string name="drawer_feed_counter_new">Количество новых выпусков</string> + <string name="drawer_feed_counter_unplayed">Количество непрослушанных выпусков</string> + <string name="drawer_feed_counter_none">Ничего</string> <!--Webview actions--> <string name="open_in_browser_label">Открыть в браузере</string> <string name="copy_url_label">Скопировать ссылку</string> @@ -37,8 +43,11 @@ <!--Other--> <string name="confirm_label">Подтвердить</string> <string name="cancel_label">Отмена</string> + <string name="yes">Да</string> + <string name="no">Нет</string> <string name="author_label">Автор</string> <string name="language_label">Язык</string> + <string name="url_label">Адрес</string> <string name="podcast_settings_label">Настройки</string> <string name="cover_label">Обложка</string> <string name="error_label">Ошибка</string> @@ -58,6 +67,7 @@ <string name="close_label">Закрыть</string> <string name="retry_label">Повторить</string> <string name="auto_download_label">Добавить в автозагрузки</string> + <string name="auto_download_apply_to_items_title">Применить к предыдущим выпускам</string> <string name="parallel_downloads_suffix">\u0020одновременных загрузок</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">URL канала</string> @@ -73,8 +83,8 @@ <string name="mark_all_read_feed_confirmation_msg">Подтвердите, что хотите пометить все эпизоды в этом канале как прослушанные.</string> <string name="show_info_label">Показать информацию</string> <string name="remove_feed_label">Удалить подкаст</string> - <string name="share_link_label">Поделиться ссылкой на сайт</string> - <string name="share_source_label">Ссылка на канал</string> + <string name="share_label">Поделиться...</string> + <string name="share_link_label">Поделиться ссылкой</string> <string name="feed_delete_confirmation_msg">Подтвердите удаление канала и всех выпусков, загруженных с этого канала.</string> <string name="feed_remover_msg">Удаление канала</string> <string name="load_complete_feed">Обновить весь канал</string> @@ -85,11 +95,12 @@ <string name="stop_label">Остановить</string> <string name="stream_label">Воспроизвести из сети</string> <string name="remove_label">Удалить</string> - <string name="remove_episode_lable">Удалить</string> - <string name="mark_read_label">Отметить как прочитанное</string> - <string name="mark_unread_label">Отметить как непрочитанное</string> + <string name="remove_episode_lable">Удалить выпуск</string> + <string name="mark_read_label">Отметить как прослушанное</string> <string name="marked_as_read_label">Помечено как прослушанное</string> + <string name="mark_unread_label">Отметить как непрослушанное</string> <string name="add_to_queue_label">Добавить в очередь</string> + <string name="added_to_queue_label">Добавлено в очередь</string> <string name="remove_from_queue_label">Удалить из очереди</string> <string name="visit_website_label">Посетить сайт</string> <string name="support_label">Поддержать через Flattr</string> @@ -202,8 +213,6 @@ <string name="pref_auto_delete_title">Автоматическое удаление</string> <string name="playback_pref">Воспроизведение</string> <string name="network_pref">Сеть</string> - <string name="pref_autoUpdateIntervall_title">Интервал обновлений</string> - <string name="pref_autoUpdateIntervall_sum">Укажите интервал через который каналы обновляются автоматически, или отключите его</string> <string name="pref_downloadMediaOnWifiOnly_sum">Загружать файлы только через Wi-Fi</string> <string name="pref_followQueue_title">Непрерывное воспроизведение</string> <string name="pref_downloadMediaOnWifiOnly_title">Загрузка по Wi-Fi</string> @@ -270,7 +279,7 @@ <!--OPML import and export--> <string name="opml_import_txtv_button_lable">OPML файлы позволяют перемещать ваши подкасты из одного менеджера подкастов в другой.</string> <string name="opml_import_explanation_1">Укажите путь к файлу на устройстве</string> - <string name="opml_import_explanation_2">Откройте OPML-файл с помощью внешних приложений: Dropbox, Google Drive или любой файловый менеджер.</string> + <string name="opml_import_explanation_2">Откройте OPML-файл с помощью внешних приложений: Dropbox, Google Drive или любого файлового менеджера.</string> <string name="opml_import_explanation_3">Множество приложений умеют <i>открывать</i> OPML-файлы <i>в</i> AntennaPod, например: Google Mail, Dropbox, Google Drive и большинство файловых менеджеров.</string> <string name="start_import_label">Начать импорт</string> <string name="opml_import_label">Импорт OPML</string> @@ -294,9 +303,6 @@ <string name="sleep_timer_label">Таймер сна</string> <string name="time_left_label">Осталось времени:\u0020</string> <string name="time_dialog_invalid_input">Неправильный ввод, время должно быть в виде числа</string> - <string name="time_unit_seconds">с</string> - <string name="time_unit_minutes">м</string> - <string name="time_unit_hours">ч</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">Категории</string> <string name="gpodnet_toplist_header">Лучшее</string> @@ -365,6 +371,7 @@ <!--Feed information screen--> <string name="authentication_label">Авторизация</string> <string name="authentication_descr">Изменить имя пользователя и пароль для этого подкаста и его выпусков.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Импорт подписок из одноцелевых приложений…</string> <string name="search_itunes_label">Поиск в iTunes</string> diff --git a/core/src/main/res/values-sv-rSE/strings.xml b/core/src/main/res/values-sv-rSE/strings.xml index c0c49ca7d..ad970e521 100644 --- a/core/src/main/res/values-sv-rSE/strings.xml +++ b/core/src/main/res/values-sv-rSE/strings.xml @@ -3,41 +3,49 @@ <!--Activitiy and fragment titles--> <string name="app_name">AntennaPod</string> <string name="feeds_label">Flöden</string> - <string name="add_feed_label">Lägg till podcast</string> + <string name="add_feed_label">Lägg till Podcast</string> <string name="podcasts_label">PODCASTS</string> <string name="episodes_label">EPISODER</string> - <string name="new_episodes_label">Nya episoder</string> - <string name="all_episodes_label">Alla episoder</string> + <string name="new_episodes_label">Nya Episoder</string> + <string name="all_episodes_label">Alla Episoder</string> <string name="new_label">Ny</string> <string name="waiting_list_label">Väntelista</string> <string name="settings_label">Inställningar</string> - <string name="add_new_feed_label">Lägg till podcast</string> + <string name="add_new_feed_label">Lägg till Podcast</string> <string name="downloads_label">Nedladdningar</string> <string name="downloads_running_label">Körs</string> <string name="downloads_completed_label">Färdiga</string> <string name="downloads_log_label">Logg</string> - <string name="cancel_download_label">Avbryt nedladdning</string> + <string name="cancel_download_label">Avbryt\nNedladdning</string> <string name="playback_history_label">Uppspelningshistorik</string> <string name="gpodnet_main_label">gpodder.net</string> - <string name="gpodnet_auth_label">gpodder.net login</string> + <string name="gpodnet_auth_label">Inloggning till gpodder.net</string> <!--New episodes fragment--> <string name="recently_published_episodes_label">Nyligen publicerade</string> - <string name="episode_filter_label">Visa bara nya episoder</string> + <string name="episode_filter_label">Visa bara nya Episoder</string> <!--Main activity--> <string name="drawer_open">Öppna meny</string> <string name="drawer_close">Stäng meny</string> <string name="drawer_preferences">Lådinställningar</string> + <string name="drawer_feed_order_unplayed_episodes">Sortera efter räknare</string> + <string name="drawer_feed_order_alphabetical">Sortera alfabetiskt</string> + <string name="drawer_feed_counter_new_unplayed">Antal nya och ospelade episoder</string> + <string name="drawer_feed_counter_new">Antal nya episoder</string> + <string name="drawer_feed_counter_unplayed">Antal ospelade episoder</string> + <string name="drawer_feed_counter_none">Inga</string> <!--Webview actions--> - <string name="open_in_browser_label">Öppna i webbläsare</string> + <string name="open_in_browser_label">Öppna i Webbläsare</string> <string name="copy_url_label">Kopiera URL</string> <string name="share_url_label">Dela URL</string> - <string name="copied_url_msg">Kopierade URL till clipboard.</string> - <string name="go_to_position_label">Gå hit</string> + <string name="copied_url_msg">Kopierade URL:en till Urklipp</string> + <string name="go_to_position_label">Gå till denna Position</string> <!--Playback history--> - <string name="clear_history_label">Rensa historik</string> + <string name="clear_history_label">Rensa Historiken</string> <!--Other--> <string name="confirm_label">Bekräfta</string> <string name="cancel_label">Avbryt</string> + <string name="yes">Ja</string> + <string name="no">Nej</string> <string name="author_label">Skapare</string> <string name="language_label">Språk</string> <string name="url_label">URL</string> @@ -60,27 +68,39 @@ <string name="close_label">Stäng</string> <string name="retry_label">Försök igen</string> <string name="auto_download_label">Inkludera i automatiska nedladdningar</string> + <string name="auto_download_apply_to_items_title">Applicera på Föregående Episoder</string> + <string name="auto_download_apply_to_items_message">Den nya inställningen <i>Automatisk Nedladdning</i> kommer automatiskt att appliceras på nya episoder.\nVill du också applicera det på tidigare episoder?</string> + <string name="auto_delete_label">Ta automatiskt bort episod\n(åsidosätter global standardinställning)</string> <string name="parallel_downloads_suffix">\u0020parallella nedladdningar</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Alltid</string> + <string name="feed_auto_download_never">Aldrig</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">Flödets URL</string> <string name="etxtFeedurlHint">URL till flöde eller webbsida</string> <string name="txtvfeedurl_label">Lägg till podcast via URL</string> - <string name="podcastdirectories_label">Hitta podcast i mapp</string> + <string name="podcastdirectories_label">Hitta Podcast i Biblioteket</string> <string name="podcastdirectories_descr">Du kan söka efter podcasts baserat på namn, kategori eller populäritet med tjänsten gpodder.net eller på iTunes Store.</string> <string name="browse_gpoddernet_label">Bläddra på gpodder.net</string> <!--Actions on feeds--> <string name="mark_all_read_label">Markera alla som spelade</string> - <string name="mark_all_read_msg">Markera alla episoder som spelade</string> + <string name="mark_all_read_msg">Markera alla Episoder som spelade</string> <string name="mark_all_read_confirmation_msg">Bekräfta att du verkligen vill markera alla episoder som spelade.</string> <string name="mark_all_read_feed_confirmation_msg">Bekräfta att du verkligen vill markera alla episoder i detta flöde som spelade.</string> + <string name="mark_all_seen_label">Markera alla som sedda</string> <string name="show_info_label">Visa information</string> - <string name="remove_feed_label">Ta bort podcast</string> - <string name="share_link_label">Dela hemsidans länk</string> - <string name="share_source_label">Dela flödeslänk</string> + <string name="remove_feed_label">Ta bort Podcast</string> + <string name="share_label">Dela...</string> + <string name="share_link_label">Dela Länk</string> + <string name="share_link_with_position_label">Dela Länk med Position</string> + <string name="share_feed_url_label">Dela Flödets URL</string> + <string name="share_item_url_label">Dela Episodens URL</string> + <string name="share_item_url_with_position_label">Dela Episodens URL med Position</string> <string name="feed_delete_confirmation_msg">Bekräfta att du vill ta bort denna feed och ALLA avsnitt av denna feed som du har hämtat.</string> - <string name="feed_remover_msg">Tar bort flöde</string> - <string name="load_complete_feed">Uppdatera hela flödet</string> - <string name="hide_episodes_title">Dölj episoder</string> + <string name="feed_remover_msg">Tar bort Flöde</string> + <string name="load_complete_feed">Uppdatera hela Flödet</string> + <string name="hide_episodes_title">Dölj Episoder</string> + <string name="episode_actions">Applicera åtgärder</string> <string name="hide_unplayed_episodes_label">Ospelade</string> <string name="hide_paused_episodes_label">Pausad</string> <string name="hide_played_episodes_label">Spelad</string> @@ -97,10 +117,10 @@ <string name="stop_label">Stopp</string> <string name="stream_label">Strömma</string> <string name="remove_label">Ta bort</string> - <string name="remove_episode_lable">Ta bort episod</string> + <string name="remove_episode_lable">Ta bort Episod</string> <string name="mark_read_label">Markera som spelad</string> - <string name="mark_unread_label">Markera som ospelad</string> <string name="marked_as_read_label">Markera som spelad</string> + <string name="mark_unread_label">Markera som ospelad</string> <string name="add_to_queue_label">Lägg till i kön</string> <string name="added_to_queue_label">Lägg till i Kö</string> <string name="remove_from_queue_label">Ta bort från Kön</string> @@ -109,24 +129,24 @@ <string name="enqueue_all_new">Lägg till alla i kön</string> <string name="download_all">Ladda ner alla</string> <string name="skip_episode_label">Hoppa över episod</string> - <string name="activate_auto_download">Aktivera automatisk nedladdning</string> - <string name="deactivate_auto_download">Avaktivera automatisk nedladdning</string> - <string name="reset_position">Nollställ uppspelningsposition</string> + <string name="activate_auto_download">Aktivera Automatisk Nedladdning</string> + <string name="deactivate_auto_download">Avaktivera Automatisk Nedladdning</string> + <string name="reset_position">Nollställ Uppspelningspositionen</string> <!--Download messages and labels--> <string name="download_successful">lyckades</string> <string name="download_failed">misslyckades</string> <string name="download_pending">Avvaktar nedladdning</string> <string name="download_running">Nedladdning pågår</string> - <string name="download_error_device_not_found">Lagringsenhet hittades inte</string> - <string name="download_error_insufficient_space">Otillräckligt utrymme</string> + <string name="download_error_device_not_found">Hittade ingen lagringsenhet</string> + <string name="download_error_insufficient_space">Otillräckligt Utrymme</string> <string name="download_error_file_error">Filfel</string> <string name="download_error_http_data_error">HTTP data fel</string> <string name="download_error_error_unknown">Okänt fel</string> <string name="download_error_parser_exception">Tolkningsfel</string> - <string name="download_error_unsupported_type">Flödestyp utan stöd</string> + <string name="download_error_unsupported_type">Flödestypen stöds inte</string> <string name="download_error_connection_error">Anslutningsfel</string> - <string name="download_error_unknown_host">Okänd värd</string> - <string name="download_error_unauthorized">Autentiseringsproblem</string> + <string name="download_error_unknown_host">Okänd Värd</string> + <string name="download_error_unauthorized">Autentiseringsfel</string> <string name="cancel_all_downloads_label">Avbryt alla nedladdningar</string> <string name="download_canceled_msg">Nedladdning avbruten</string> <string name="download_canceled_autodownload_enabled_msg">Nedladdning avbruten\nStängde av <i>Automatisk nedladdning</i> för denna sak</string> @@ -134,13 +154,13 @@ <string name="download_report_content_title">Nedladdningsrapport</string> <string name="download_error_malformed_url">Felaktig webbadress</string> <string name="download_error_io_error">IO fel</string> - <string name="download_error_request_error">Request fel</string> - <string name="download_error_db_access">Ingen tillgång till databasen</string> + <string name="download_error_request_error">Förfrågningsfel</string> + <string name="download_error_db_access">Åtkomstfel till databasen</string> <string name="downloads_left">\u0020Nedladdningar kvar</string> <string name="downloads_processing">Bearbetar nedladdningar</string> <string name="download_notification_title">Laddar ner podcastdata</string> <string name="download_report_content">%1$d nedladdningar lyckades, %2$d misslyckades</string> - <string name="download_log_title_unknown">Okänd titel</string> + <string name="download_log_title_unknown">Okänd Titel</string> <string name="download_type_feed">Flöde</string> <string name="download_type_media">Mediafil</string> <string name="download_type_image">Bild</string> @@ -148,10 +168,10 @@ <string name="authentication_notification_title">Autentisering krävs</string> <string name="authentication_notification_msg">Resursen du begärde kräver ett användarnamn och ett lösenord</string> <string name="confirm_mobile_download_dialog_title">Bekräfta mobil nedladdning</string> - <string name="confirm_mobile_download_dialog_message_not_in_queue">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nAktivera tillfälligt eller bara lägg till i kön?\n\n<small>Ditt val gäller i 10 minuter.</small></string> - <string name="confirm_mobile_download_dialog_message">Nedladdning över mobil dataanslutning är avstängt i inställningarna.\n\nAktivera tillfälligt?\n\n<small>Ditt val gäller i 10 minuter.</small></string> - <string name="confirm_mobile_download_dialog_only_add_to_queue">Lägg bara till i kön</string> - <string name="confirm_mobile_download_dialog_enable_temporarily">Aktivera tillfälligt</string> + <string name="confirm_mobile_download_dialog_message_not_in_queue">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nDu kan välja att antingen bara lägga till episoden i kön eller att tillfälligt tillåta nedladdning.\n\n<small>Ditt val gäller i 10 minuter.</small></string> + <string name="confirm_mobile_download_dialog_message">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nVill du tillfälligt tillåta nedladdning?\n\n<small>Ditt val gäller i 10 minuter.</small></string> + <string name="confirm_mobile_download_dialog_only_add_to_queue">Köa</string> + <string name="confirm_mobile_download_dialog_enable_temporarily">Tillåt tillfälligt</string> <!--Mediaplayer messages--> <string name="player_error_msg">Fel! </string> <string name="player_stopped_msg">Inget media spelar</string> @@ -166,9 +186,9 @@ <string name="playbackservice_notification_title">Spelar podcast</string> <string name="unknown_media_key">AntannaPod - Okänd mediaknapp: %1$d</string> <!--Queue operations--> - <string name="lock_queue">Lås kön</string> - <string name="unlock_queue">Lås upp kön</string> - <string name="clear_queue_label">Rensa kön</string> + <string name="lock_queue">Lås Kön</string> + <string name="unlock_queue">Lås upp Kön</string> + <string name="clear_queue_label">Rensa Kön</string> <string name="undo">Ångra</string> <string name="removed_from_queue">Föremålet avlägsnades</string> <string name="move_to_top_label">Flytta längst upp</string> @@ -229,37 +249,47 @@ <string name="pref_smart_mark_as_played_title">Smart markering av uppspelat innehåll</string> <string name="playback_pref">Uppspelning</string> <string name="network_pref">Nätverk </string> - <string name="pref_autoUpdateIntervall_title">Uppdateringsintervall</string> - <string name="pref_autoUpdateIntervall_sum">Ange ett intervall för att automatiskt uppdatera flödet eller avaktivera det</string> + <string name="pref_autoUpdateIntervallOrTime_title">Uppdateringsintervall eller Tid på Dagen</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Ange ett intervall eller specifik tid på dagen för att uppdatera flödena automatisk.</string> + <string name="pref_autoUpdateIntervallOrTime_message">Du kan välja ett <i>intervall</i> som \"var 2 timmar\", en specifik <i>tid på dagen</i> som \"07:00\" eller <i>avaktivera</i> automatiska uppdateringar helt.\n\n<small>Notera: Uppdateringstiderna är inte exakta. Korta fördröjningar kan uppstå.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Avaktivera</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Sätt intervall</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Sätt Tid på Dagen</string> <string name="pref_downloadMediaOnWifiOnly_sum">Hämta mediefiler endast över WiFi</string> - <string name="pref_followQueue_title">Kontinuerlig uppspelning</string> + <string name="pref_followQueue_title">Kontinuerlig Uppspelning</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi nedladdning</string> - <string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar bortkopplade</string> - <string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar återanslutna</string> - <string name="pref_mobileUpdate_title">Mobila uppdateringar</string> + <string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar Bortkopplade</string> + <string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar Återanslutna</string> + <string name="pref_mobileUpdate_title">Mobila Uppdateringar</string> <string name="pref_mobileUpdate_sum">Tillåt uppdateringar via mobil dataanslutning</string> <string name="refreshing_label">Uppdaterar</string> <string name="flattr_settings_label">Flattr inställningar</string> <string name="pref_flattr_auth_title">Flattr inloggning</string> <string name="pref_flattr_auth_sum">För att Flattra saker direkt från appen, logga in på ditt Flattr-konto.</string> - <string name="pref_flattr_this_app_title">Flattra den här appen</string> + <string name="pref_flattr_this_app_title">Flattra denna App</string> <string name="pref_flattr_this_app_sum">Stöd utvecklingen av AntennaPod genom att flattra den. Tack!</string> <string name="pref_revokeAccess_title">Återkalla åtkomst</string> <string name="pref_revokeAccess_sum">Återkalla behörigheten till ditt Flattr-konto för denna app.</string> <string name="pref_auto_flattr_title">Automatisk Flattring</string> <string name="pref_auto_flattr_sum">Konfigurerar automatisk Flattring</string> <string name="user_interface_label">Användargränssnitt</string> - <string name="pref_set_theme_title">Välj tema</string> - <string name="pref_nav_drawer_items_title">Ändra navigationslådan</string> + <string name="pref_set_theme_title">Välj Tema</string> + <string name="pref_nav_drawer_title">Anpassa Navigeringsrutan</string> + <string name="pref_nav_drawer_sum">Anpassa utseendet på navigeringsrutan.</string> + <string name="pref_nav_drawer_items_title">Välj saker i Navigeringsrutan</string> <string name="pref_nav_drawer_items_sum">Ändra vilka saker som visas i navigationslådan.</string> + <string name="pref_nav_drawer_feed_order_title">Välj Prenumerationsordning</string> + <string name="pref_nav_drawer_feed_order_sum">Ändra ordningen på dina prenumerationer</string> + <string name="pref_nav_drawer_feed_counter_title">Välj Prenumerationsräknaren</string> + <string name="pref_nav_drawer_feed_counter_sum">Ändra informationen som visas av prenumerationsräknaren</string> <string name="pref_set_theme_sum">Ändra utseendet på AntennaPod.</string> - <string name="pref_automatic_download_title">Automatisk nedladdning</string> + <string name="pref_automatic_download_title">Automatisk Nedladdning</string> <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="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_parallel_downloads_title">Parallella Nedladdningar</string> <string name="pref_episode_cache_title">Episodcache</string> <string name="pref_theme_title_light">Ljust</string> <string name="pref_theme_title_dark">Mörkt</string> @@ -281,12 +311,16 @@ <string name="pref_gpodnet_sethostname_use_default_host">Använd standardvärden</string> <string name="pref_expandNotify_title">Expandera notifieringar</string> <string name="pref_expandNotify_sum">Expandera alltid notifieringen för att visa uppspelningskontrollerna.</string> - <string name="pref_persistNotify_title">Bestående uppspelningskontroller</string> + <string name="pref_persistNotify_title">Bestående Uppspelningskontroller</string> <string name="pref_persistNotify_sum">Behåll notifiering och kontroller på låsskärmen när uppspelningen pausas.</string> + <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 notifieringar.</string> <string name="pref_queueAddToFront_sum">Lägg till episoder först i kön.</string> - <string name="pref_queueAddToFront_title">Köa först.</string> + <string name="pref_queueAddToFront_title">Köa Först</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> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Aktivera automatisk Flattring</string> <string name="auto_flattr_after_percent">Flattra episoden så snart %d procent har spelats</string> @@ -305,19 +339,20 @@ <string name="opml_import_explanation_2">Använd en extern applikation som Dropbox, Google Drive eller ditt favoritval av filhanterare för att öppna en OPML fil.</string> <string name="opml_import_explanation_3">Flera applikationer som Google Mail, Dropbox, Google Drive och de flesta filhanterare kan <i>öppna</i> OPML filer <i>med</i> AntennaPod.</string> <string name="start_import_label">Påbörja importering</string> - <string name="opml_import_label">Importera OPML-fil</string> + <string name="opml_import_label">OPML Importering</string> <string name="opml_directory_error">FEL! </string> <string name="reading_opml_label">Läser OPML-fil</string> <string name="opml_reader_error">Ett fel har skett vid iläsning av opml dokumentet:</string> <string name="opml_import_error_dir_empty">Katalogen är tom.</string> <string name="select_all_label">Välj alla</string> <string name="deselect_all_label">Avmarkera alla</string> + <string name="select_options_label">Välj ...</string> <string name="choose_file_from_filesystem">Från lokalt filsystem</string> <string name="choose_file_from_external_application">Använd extern applikation</string> <string name="opml_export_label">OPML export</string> <string name="exporting_label">Exporterar...</string> <string name="export_error_label">Exporteringsfel</string> - <string name="opml_export_success_title">OPML export lyckades</string> + <string name="opml_export_success_title">OPML Exportering lyckades.</string> <string name="opml_export_success_sum">.opml filen skrevs till:\u0020</string> <!--Sleep timer--> <string name="set_sleeptimer_label">Ställ in sömntimer</string> @@ -326,9 +361,21 @@ <string name="sleep_timer_label">Sömntimer</string> <string name="time_left_label">Återstående tid:\u0020</string> <string name="time_dialog_invalid_input">Ogiltigt tal, tiden måste vara ett heltal</string> - <string name="time_unit_seconds">sekunder</string> - <string name="time_unit_minutes">minuter</string> - <string name="time_unit_hours">timmar</string> + <string name="time_seconds">sekunder</string> + <string name="time_minutes">minuter</string> + <string name="time_hours">timmar</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 sekund</item> + <item quantity="other">%d sekunder</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minut</item> + <item quantity="other">%d minuter</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 timme</item> + <item quantity="other">%d timmar</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORIER</string> <string name="gpodnet_toplist_header">BÄSTA PODCASTS</string> @@ -360,7 +407,7 @@ <!--Directory chooser--> <string name="selected_folder_label">Vald mapp:</string> <string name="create_folder_label">Skapa mapp</string> - <string name="choose_data_directory">Välj mapp</string> + <string name="choose_data_directory">Välj Datakatalog</string> <string name="create_folder_msg">Skapa ny mapp med namnet \"%1$s\"?</string> <string name="create_folder_success">Skapade ny mapp</string> <string name="create_folder_error_no_write_access">Kan inte skriva till den här mappen</string> @@ -370,9 +417,10 @@ <string name="folder_not_empty_dialog_msg">Den mapp du har valt är inte tom. Filer kommer att placeras direkt i denna mapp. Fortsätt ändå?</string> <string name="set_to_default_folder">Välj standardmapp</string> <string name="pref_pausePlaybackForFocusLoss_sum">Pausa uppspelning istället för att sänka volymen när en annan app vill spela ljud</string> - <string name="pref_pausePlaybackForFocusLoss_title">Pausa för avbrott</string> + <string name="pref_pausePlaybackForFocusLoss_title">Pausa för Avbrott</string> <string name="pref_resumeAfterCall_sum">Återuppta uppspelning när ett telefonsamtal avslutas</string> - <string name="pref_resumeAfterCall_title">Återuppta efter samtal</string> + <string name="pref_resumeAfterCall_title">Fortsätt efter Samtal</string> + <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="subscribed_label">Prenumererar</string> @@ -399,7 +447,29 @@ <!--Feed information screen--> <string name="authentication_label">Autentisering</string> <string name="authentication_descr">Byt ditt användarnamn och lösenord för den här podcasten och dess episoder.</string> + <!--Progress information--> + <string name="progress_upgrading_database">Uppgraderar databasen</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Importerar prenumerationer från appar gjorda för ett enda syfte...</string> <string name="search_itunes_label">Leta i iTunes</string> + <string name="select_label"><b>Välj ...</b></string> + <string name="all_label">Alla</string> + <string name="selected_all_label">Välj alla Episoder</string> + <string name="none_label">Inga</string> + <string name="deselected_all_label">Avmarkera alla Episoder</string> + <string name="played_label">Spelade</string> + <string name="selected_played_label">Valde spelade Episoder</string> + <string name="unplayed_label">Ospelade</string> + <string name="selected_unplayed_label">Valde ospelade Episoder</string> + <string name="downloaded_label">Nedladdade</string> + <string name="selected_downloaded_label">Valde nedladdade Episoder</string> + <string name="not_downloaded_label">Ej nedladdade</string> + <string name="selected_not_downloaded_label">Valde ej nedladdade Episoder</string> + <string name="sort_title"><b>Sortera efter ...</b></string> + <string name="sort_title_a_z">Titel (A \u2192 Ö)</string> + <string name="sort_title_z_a">Titel (Ö \u2192 A)</string> + <string name="sort_date_new_old">Datum (Ny \u2192 Gammal)</string> + <string name="sort_date_old_new">Datum (Gammal \u2192 Ny)</string> + <string name="sort_duration_short_long">Längd (Kort \u2192 Lång)</string> + <string name="sort_duration_long_short">Längd (Lång \u2192 Kort)</string> </resources> diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml index e83c9b48e..d202bc67c 100644 --- a/core/src/main/res/values-tr/strings.xml +++ b/core/src/main/res/values-tr/strings.xml @@ -26,6 +26,7 @@ <!--Main activity--> <string name="drawer_open">Münüyü aç</string> <string name="drawer_close">Menüyü kapat</string> + <string name="drawer_preferences">Çekmece Seçenekleri</string> <!--Webview actions--> <string name="open_in_browser_label">Tarayıcıda aç</string> <string name="copy_url_label">URL\'yi kopyala</string> @@ -39,6 +40,7 @@ <string name="cancel_label">İptal</string> <string name="author_label">Yayıncı</string> <string name="language_label">Dil</string> + <string name="url_label">URL</string> <string name="podcast_settings_label">Ayarlar</string> <string name="cover_label">Resim</string> <string name="error_label">Hata</string> @@ -67,17 +69,26 @@ <string name="podcastdirectories_descr">gdpodder.net dizininde yeni cep yayınlarını isme, kategoriye veya popülerliğe göre arayabilirsiniz veya iTunes mağazasında arama yapabilirsiniz.</string> <string name="browse_gpoddernet_label">gpodder.net\'e gözat</string> <!--Actions on feeds--> - <string name="mark_all_read_label">Hepsini okundu olarak işaretle</string> - <string name="mark_all_read_msg">Tüm bölümler okundu olarak işaretlendi</string> - <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string> - <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string> + <string name="mark_all_read_label">Hepsini oynatıldı olarak işaretle</string> + <string name="mark_all_read_msg">Tüm bölümleri oynatıldı olarak işaretle</string> + <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string> + <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string> <string name="show_info_label">Bilgiyi göster</string> <string name="remove_feed_label">Cep yayını kaldır</string> <string name="share_link_label">Web sayfası bağlantısı paylaş</string> - <string name="share_source_label">Besleme bağlantısını paylaş</string> <string name="feed_delete_confirmation_msg">Lütfen bu beslemeyi ve bu beslemeye ait indirilmiş BÜTÜN bölümleri silme isteğinizi onaylayın.</string> <string name="feed_remover_msg">Besleme kaldırılıyor</string> <string name="load_complete_feed">Tüm beslemeyi yenile</string> + <string name="hide_episodes_title">Bölümleri gizle</string> + <string name="hide_unplayed_episodes_label">Oynatılmadı</string> + <string name="hide_paused_episodes_label">Duraklatıldı</string> + <string name="hide_played_episodes_label">Oynatıldı</string> + <string name="hide_queued_episodes_label">Kuyrukta</string> + <string name="hide_not_queued_episodes_label">Kuyrukta değil</string> + <string name="hide_downloaded_episodes_label">İndirildi</string> + <string name="hide_not_downloaded_episodes_label">İndirilmedi</string> + <string name="filtered_label">Filtrelendi</string> + <string name="refresh_failed_msg">{fa-exclamation-circle} Son yenileme başarısız oldu</string> <!--actions on feeditems--> <string name="download_label">İndir</string> <string name="play_label">Oynat</string> @@ -86,16 +97,20 @@ <string name="stream_label">Akış</string> <string name="remove_label">Kaldır</string> <string name="remove_episode_lable">Bölümü kaldır</string> - <string name="mark_read_label">Okundu olarak işaretle</string> - <string name="mark_unread_label">Okunmadı olarak işaretle</string> - <string name="marked_as_read_label">Okundu olarak işaretlendi</string> + <string name="mark_read_label">Oynatıldı olarak işaretle</string> + <string name="marked_as_read_label">Oynatıldı olarak işaretlendi</string> + <string name="mark_unread_label">Oynatılmadı olarak işaretle</string> <string name="add_to_queue_label">Kuyruğa Ekle</string> + <string name="added_to_queue_label">Kuyruğa Eklendi</string> <string name="remove_from_queue_label">Kuyruktan Kaldır</string> <string name="visit_website_label">Siteyi Ziyaret Et</string> <string name="support_label">Flattr ile destekle</string> <string name="enqueue_all_new">Hepsini kuyruğa ekle</string> <string name="download_all">Hepsini indir</string> <string name="skip_episode_label">Bölümü atla</string> + <string name="activate_auto_download">Otomatik indirmeyi etkinleştir</string> + <string name="deactivate_auto_download">Otomatik indirmeyi devre dışı bırak</string> + <string name="reset_position">Çalme konumunu sıfırla</string> <!--Download messages and labels--> <string name="download_successful">başarılı</string> <string name="download_failed">başarısız</string> @@ -113,7 +128,9 @@ <string name="download_error_unauthorized">Yetkilendirme hatası</string> <string name="cancel_all_downloads_label">Bütün indirmeleri iptal et</string> <string name="download_canceled_msg">İndirme iptal edildi</string> - <string name="download_report_title">İndirme tamamlandı</string> + <string name="download_canceled_autodownload_enabled_msg">İndirme iptal edildi\nBu öğe için <i>Otomatik İndirme</i> devre dışı</string> + <string name="download_report_title">İndirme hata(lar) ile tamamlandı</string> + <string name="download_report_content_title">İndirme raporu</string> <string name="download_error_malformed_url">Bozuk URL</string> <string name="download_error_io_error">G/Ç Hatası</string> <string name="download_error_request_error">İstek hatası</string> @@ -129,6 +146,11 @@ <string name="download_request_error_dialog_message_prefix">Dosyayı indirmeye çalışırken bir hata oluştu:\u0020</string> <string name="authentication_notification_title">Yetkilendirme gerekiyor</string> <string name="authentication_notification_msg">İstediğiniz kaynak kullanıcı adı ve şifre istiyor</string> + <string name="confirm_mobile_download_dialog_title">Mobil İndirmeyi Onaylayın</string> + <string name="confirm_mobile_download_dialog_message_not_in_queue">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı yoksa sadece kuyruğa mı eklensin?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string> + <string name="confirm_mobile_download_dialog_message">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string> + <string name="confirm_mobile_download_dialog_only_add_to_queue">Sadece Kuyruğa ekle</string> + <string name="confirm_mobile_download_dialog_enable_temporarily">Geçici olarak aç</string> <!--Mediaplayer messages--> <string name="player_error_msg">Hata!</string> <string name="player_stopped_msg">Çalınan medya yok</string> @@ -143,6 +165,8 @@ <string name="playbackservice_notification_title">Cep yayını çalınıyor</string> <string name="unknown_media_key">AntennaPod - Bilinmeyen medya anahtarı: %1$d</string> <!--Queue operations--> + <string name="lock_queue">Kuyruğu kilitle</string> + <string name="unlock_queue">Kuyruğun kilidini aç</string> <string name="clear_queue_label">Kuyruğu temizle</string> <string name="undo">Geri al</string> <string name="removed_from_queue">Öge kaldırıldı</string> @@ -202,8 +226,6 @@ <string name="pref_auto_delete_title">Otomatik Silme</string> <string name="playback_pref">Çalma</string> <string name="network_pref">Ağ</string> - <string name="pref_autoUpdateIntervall_title">Güncelleme aralığı</string> - <string name="pref_autoUpdateIntervall_sum">Beslemeleri yenilemek için bir aralık belirtin veya devre dışı bırakın.</string> <string name="pref_downloadMediaOnWifiOnly_sum">Medya dosyalarını sadece kablosuz bağlantı üzerinden indir</string> <string name="pref_followQueue_title">Devamlı çalma</string> <string name="pref_downloadMediaOnWifiOnly_title">Kablosuz medya indirmesi</string> @@ -246,8 +268,8 @@ <string name="pref_gpodnet_setlogin_information_sum">gpodder.net hesabınız için giriş bilgisini değiştirin.</string> <string name="pref_playback_speed_title">Çalma hızları</string> <string name="pref_playback_speed_sum">Değişken hızlı ses yürütmesi için kullanılabilir hızları özelleştirin</string> - <string name="pref_seek_delta_title">Arama zamanı</string> - <string name="pref_seek_delta_sum">Geri veya ileri sararken bu kadar saniye atla</string> + <string name="pref_fast_forward">İleri sarma süresi</string> + <string name="pref_rewind">Geri sarma süresi</string> <string name="pref_gpodnet_sethostname_title">Sunucu ismini ayarla</string> <string name="pref_gpodnet_sethostname_use_default_host">Varsayılan sunucuyu kullan</string> <string name="pref_expandNotify_title">Bildirimi Genişlet</string> @@ -257,6 +279,7 @@ <string name="pref_expand_notify_unsupport_toast">Android 4.1 öncesi sürümler genişletilmiş bildirimleri desteklememektedir.</string> <string name="pref_queueAddToFront_sum">Yeni bölümleri kuyruğun önüne ekle.</string> <string name="pref_queueAddToFront_title">Kuyruğun önüne ekle.</string> + <string name="pref_smart_mark_as_played_disabled">Devre dışı</string> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Otomatik Flattr\'lamayı etkinleştir</string> <string name="auto_flattr_after_percent">Bölümün yüzde %d kısmı oynatıldığında Flattr\'la</string> @@ -296,9 +319,6 @@ <string name="sleep_timer_label">Zamanlayıcı</string> <string name="time_left_label">Kalan süre:\u0020</string> <string name="time_dialog_invalid_input">Geçersiz giriş, zaman bir tam sayı olmalıdır</string> - <string name="time_unit_seconds">saniye</string> - <string name="time_unit_minutes">dakika</string> - <string name="time_unit_hours">saat</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">KATEGORİLER</string> <string name="gpodnet_toplist_header">POPÜLER CEP YAYINLARI</string> @@ -341,6 +361,8 @@ <string name="set_to_default_folder">Vaysayılan dizini seç</string> <string name="pref_pausePlaybackForFocusLoss_sum">Başka bir uygulama ses çalmak istediğinde sesi kısmak yerine yürütmeyi duraklat</string> <string name="pref_pausePlaybackForFocusLoss_title">Kesintiler için duraklat</string> + <string name="pref_resumeAfterCall_sum">Bir telefon konuşması tamamlandıktan sonra çalmaya kaldığı yerden devam et</string> + <string name="pref_resumeAfterCall_title">Konuşmadan sonra devam et</string> <!--Online feed view--> <string name="subscribe_label">Üye ol</string> <string name="subscribed_label">Üye olundu</string> @@ -367,6 +389,7 @@ <!--Feed information screen--> <string name="authentication_label">Yetkilendirme</string> <string name="authentication_descr">Bu cep yayını ve içerdiği bölümler için kullanıcı adı şifreyi değiştir.</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Üyelikler tek-amaçlı uygulamalardan içe aktarılıyor...</string> <string name="search_itunes_label">iTunes\'da Arama</string> diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml index b6cd8ca98..9715e40c3 100644 --- a/core/src/main/res/values-uk-rUA/strings.xml +++ b/core/src/main/res/values-uk-rUA/strings.xml @@ -27,6 +27,12 @@ <string name="drawer_open">Показати меню</string> <string name="drawer_close">Сховати меню</string> <string name="drawer_preferences">Настройки навігації</string> + <string name="drawer_feed_order_unplayed_episodes">Сортувати за лічильником</string> + <string name="drawer_feed_order_alphabetical">Сортування за абеткою</string> + <string name="drawer_feed_counter_new_unplayed">Кількість нових та непрослуханих епізодів</string> + <string name="drawer_feed_counter_new">Кількість нових епізодів</string> + <string name="drawer_feed_counter_unplayed">Кількість непрослуханих епізодів</string> + <string name="drawer_feed_counter_none">Жодних</string> <!--Webview actions--> <string name="open_in_browser_label">Відкрити в браузері</string> <string name="copy_url_label">Копіювати URL</string> @@ -38,6 +44,8 @@ <!--Other--> <string name="confirm_label"> Підтвердити</string> <string name="cancel_label">Скасувати</string> + <string name="yes">Так</string> + <string name="no">Ні</string> <string name="author_label">Автор</string> <string name="language_label">Мова</string> <string name="url_label">URL</string> @@ -60,7 +68,13 @@ <string name="close_label">Закрити</string> <string name="retry_label">Повторити знову</string> <string name="auto_download_label">Включити до автозавантаження</string> + <string name="auto_download_apply_to_items_title">Застосувати до попередніх епізодів</string> + <string name="auto_download_apply_to_items_message">Нове налаштування <i>Автозавантаження</i> буде автоматично застосоване до нових епізодів.\nБажаєте також застосувати його до попередніх епізодів?</string> + <string name="auto_delete_label">Автоматичне видалення епізода\n(перевизначити глобальне налаштування за замовчуванням)</string> <string name="parallel_downloads_suffix">\u0020паралельні завантаження</string> + <string name="feed_auto_download_global">Для всіх</string> + <string name="feed_auto_download_always">Завжди</string> + <string name="feed_auto_download_never">Ніколи</string> <!--'Add Feed' Activity labels--> <string name="feedurl_label">Посилання на канал</string> <string name="etxtFeedurlHint">URL канала або сайта</string> @@ -73,14 +87,20 @@ <string name="mark_all_read_msg">Позначено всі епізоди як грані</string> <string name="mark_all_read_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте позначити всі епізоди як грані.</string> <string name="mark_all_read_feed_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте позначити всі епізоди цього канала як грані.</string> + <string name="mark_all_seen_label">Позначити всі як переглянуті</string> <string name="show_info_label">Інформація</string> <string name="remove_feed_label">Видалити подкаст</string> + <string name="share_label">Поділитись...</string> <string name="share_link_label">Поділитися URL сайту</string> - <string name="share_source_label">Поділитися URL каналу</string> + <string name="share_link_with_position_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> <string name="feed_delete_confirmation_msg">Ви впенені що хочете видаліти канал та всі завантажені епізоди</string> <string name="feed_remover_msg">Удаляю канал</string> <string name="load_complete_feed">Оновити канал цілком</string> <string name="hide_episodes_title">Приховати епізоди</string> + <string name="episode_actions">Застосувати дії</string> <string name="hide_unplayed_episodes_label">Неграні</string> <string name="hide_paused_episodes_label">На паузі</string> <string name="hide_played_episodes_label">Грані</string> @@ -99,8 +119,8 @@ <string name="remove_label">Видалити</string> <string name="remove_episode_lable">Видалити епізод</string> <string name="mark_read_label">Позначити як граний</string> - <string name="mark_unread_label">Позначити як не граний</string> <string name="marked_as_read_label">Позначено як граний</string> + <string name="mark_unread_label">Позначити як не граний</string> <string name="add_to_queue_label">Додати до черги</string> <string name="added_to_queue_label">Додано до черги</string> <string name="remove_from_queue_label">Видалити з черги</string> @@ -229,8 +249,12 @@ <string name="pref_smart_mark_as_played_title">Розумне позначення граних епізодів</string> <string name="playback_pref">Відтворення</string> <string name="network_pref">Мережа</string> - <string name="pref_autoUpdateIntervall_title">Частота оновлень</string> - <string name="pref_autoUpdateIntervall_sum">Визначити інтервал часу для автооновлювання або відключити автооновлення</string> + <string name="pref_autoUpdateIntervallOrTime_title">Частота оновлень або оновлення в зазначений час</string> + <string name="pref_autoUpdateIntervallOrTime_sum">Визначити інтервал часу або визначити час щодня для автооновлення</string> + <string name="pref_autoUpdateIntervallOrTime_message">Можливо встановити <i>інтервал</i> як то \"кожні 2 години\", встановити <i>час щодня</i> як то \"7:00\" або <i>відклюсити</i> автоматичне оновлення взагалі.\n\n<small>Зверніть увагу: Час оновлення не є точним. Можливі короткі затримки.</small></string> + <string name="pref_autoUpdateIntervallOrTime_Disable">Вимкнути</string> + <string name="pref_autoUpdateIntervallOrTime_Interval">Встановити інтервал</string> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Встановити час щодня</string> <string name="pref_downloadMediaOnWifiOnly_sum">Завантажувати тільки через Wifi</string> <string name="pref_followQueue_title">Грати безперервно</string> <string name="pref_downloadMediaOnWifiOnly_title">Завантаження через Wifi</string> @@ -250,8 +274,14 @@ <string name="pref_auto_flattr_sum">Налаштування автоматичного заохочення авторів через сервіс flattr</string> <string name="user_interface_label">Вигляд</string> <string name="pref_set_theme_title">Обрати тему</string> + <string name="pref_nav_drawer_title">Налаштувати панель навігації</string> + <string name="pref_nav_drawer_sum">Налаштувати вигляд панелі навігації</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_set_theme_sum">Змінити вигляд AntennaPod</string> <string name="pref_automatic_download_title">Автоматичне завантаження</string> <string name="pref_automatic_download_sum">Налаштування автоматичного завантаження епізодів</string> @@ -283,10 +313,14 @@ <string name="pref_expandNotify_sum">Завжди розгортати повідомлення, щоб показати кнопки керування.</string> <string name="pref_persistNotify_title">Завжди показувати елементи керування відтворенням</string> <string name="pref_persistNotify_sum">Показувати повідомлення та елементи керування на lockscreen в режимі паузи.</string> + <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_queueAddToFront_sum">Додавати нові епізоди до початку черги.</string> <string name="pref_queueAddToFront_title">Додавати в початок черги.</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> <!--Auto-Flattr dialog--> <string name="auto_flattr_enable">Включити автоматичне заохочення авторів через сервіс flattr</string> <string name="auto_flattr_after_percent">Заохотити автора через Flattr щойно %d відсотків епізода було відтворено</string> @@ -312,6 +346,7 @@ <string name="opml_import_error_dir_empty">Директорія імпорту пуста</string> <string name="select_all_label">Обрати все</string> <string name="deselect_all_label">Убрати виділення</string> + <string name="select_options_label">Обрати ...</string> <string name="choose_file_from_filesystem">З локальної файлової системи</string> <string name="choose_file_from_external_application">За допомогою додатка</string> <string name="opml_export_label">OPML экспорт</string> @@ -326,9 +361,24 @@ <string name="sleep_timer_label">Таймер сну</string> <string name="time_left_label">Залишилось:\u0020</string> <string name="time_dialog_invalid_input">Помилка вводу, час повинен бути цілим</string> - <string name="time_unit_seconds">секунд</string> - <string name="time_unit_minutes">хвилин</string> - <string name="time_unit_hours">годин</string> + <string name="time_seconds">секунд</string> + <string name="time_minutes">хвилин</string> + <string name="time_hours">годин</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 секунда</item> + <item quantity="few">%d секунди</item> + <item quantity="other">%d секунд</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 хвилина</item> + <item quantity="few">%d хвилини</item> + <item quantity="other">%d хвилин</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 година</item> + <item quantity="few">%d години</item> + <item quantity="other">%d годин</item> + </plurals> <!--gpodder.net--> <string name="gpodnet_taglist_header">КАТЕГОРІЇ</string> <string name="gpodnet_toplist_header">ТОП ПОДКАСТІВ</string> @@ -373,6 +423,7 @@ <string name="pref_pausePlaybackForFocusLoss_title">Пауза в разі переривання</string> <string name="pref_resumeAfterCall_sum">Відновити відтворення після закінчення дзвінка</string> <string name="pref_resumeAfterCall_title">Відновити після дзвінка</string> + <string name="pref_restart_required">Для застосування змін потрібно перезапустити AntennaPod</string> <!--Online feed view--> <string name="subscribe_label">Підписатися</string> <string name="subscribed_label">Підписано</string> @@ -399,7 +450,29 @@ <!--Feed information screen--> <string name="authentication_label">Автентикація</string> <string name="authentication_descr">Змінити ваші логін та пароль для подкаста та епізодів</string> + <!--Progress information--> + <string name="progress_upgrading_database">Оновлення бази даних</string> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">Імпорт подкастів з інших програм...</string> <string name="search_itunes_label">Пошук в iTunes</string> + <string name="select_label"><b>Обрати ...</b></string> + <string name="all_label">Всі</string> + <string name="selected_all_label">Обрано всі епізоди</string> + <string name="none_label">Жодного</string> + <string name="deselected_all_label">Жодного епізода обрано</string> + <string name="played_label">Переглянуті</string> + <string name="selected_played_label">Обрано переглянуті епізоди</string> + <string name="unplayed_label">Непереглянуті</string> + <string name="selected_unplayed_label">Обрано непереглянуті епізоди</string> + <string name="downloaded_label">Завантажені</string> + <string name="selected_downloaded_label">Обрано завантажені епізоди</string> + <string name="not_downloaded_label">Незавантажені</string> + <string name="selected_not_downloaded_label">Обрано незавантажені епізоди</string> + <string name="sort_title"><b>Сортувати за ...</b></string> + <string name="sort_title_a_z">Назва (А \u2192 Я)</string> + <string name="sort_title_z_a">Назва (Я \u2192 А)</string> + <string name="sort_date_new_old">Дата (Нові \u2192 Старі)</string> + <string name="sort_date_old_new">Дата (Старі \u2192 Нові)</string> + <string name="sort_duration_short_long">Тривалість (Короткі \u2192 Довгі)</string> + <string name="sort_duration_long_short">Тривалість (Довгі \u2192 Короткі)</string> </resources> diff --git a/core/src/main/res/values-zh-rCN/strings.xml b/core/src/main/res/values-zh-rCN/strings.xml index 594249a31..332c8ed4c 100644 --- a/core/src/main/res/values-zh-rCN/strings.xml +++ b/core/src/main/res/values-zh-rCN/strings.xml @@ -72,7 +72,6 @@ <string name="show_info_label">查看信息</string> <string name="remove_feed_label">删除播客</string> <string name="share_link_label">分享网站链接</string> - <string name="share_source_label">分享订阅链接</string> <string name="feed_delete_confirmation_msg">确认要删除这些订阅吗? 该订阅所有已经下载的曲目将一并删除. </string> <string name="feed_remover_msg">删除订阅</string> <string name="load_complete_feed">刷新全部订阅</string> @@ -199,8 +198,6 @@ <string name="pref_auto_delete_title">自动删除</string> <string name="playback_pref">播放</string> <string name="network_pref">网络</string> - <string name="pref_autoUpdateIntervall_title">更新周期</string> - <string name="pref_autoUpdateIntervall_sum">设置订阅自动刷新周期</string> <string name="pref_downloadMediaOnWifiOnly_sum">仅在 WIFI 情况下载媒体文件</string> <string name="pref_followQueue_title">连续播放</string> <string name="pref_downloadMediaOnWifiOnly_title">仅在 WIFI 情况下载</string> @@ -292,9 +289,6 @@ <string name="sleep_timer_label">休眠计时器</string> <string name="time_left_label">计时剩余:\u0020</string> <string name="time_dialog_invalid_input">无效的输入, 时间是一个整数</string> - <string name="time_unit_seconds">秒</string> - <string name="time_unit_minutes">分钟</string> - <string name="time_unit_hours">小时</string> <!--gpodder.net--> <string name="gpodnet_taglist_header">目录</string> <string name="gpodnet_toplist_header">头条播客</string> @@ -363,6 +357,7 @@ <!--Feed information screen--> <string name="authentication_label">验证</string> <string name="authentication_descr">给本播客及曲目变更用户名及密码</string> + <!--Progress information--> <!--AntennaPodSP--> <string name="sp_apps_importing_feeds_msg">正在从选定的应用中导入订阅...</string> <string name="search_itunes_label">搜索 iTunes</string> diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index 4ecf2cf61..8f1268993 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -1,6 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <string-array name="spnAutoDeleteItems"> + <item>@string/feed_auto_download_global</item> + <item>@string/feed_auto_download_always</item> + <item>@string/feed_auto_download_never</item> + </string-array> + <string-array name="smart_mark_as_played_values"> <item>0</item> <item>15</item> @@ -20,18 +26,7 @@ <item>60</item> </integer-array> - <string-array name="update_intervall_options"> - <item>Manual</item> - <item>1 hour</item> - <item>2 hours</item> - <item>4 hours</item> - <item>8 hours</item> - <item>12 hours</item> - <item>24 hours</item> - </string-array> - <string-array name="update_intervall_values"> - <item>0</item> <item>1</item> <item>2</item> <item>4</item> @@ -39,30 +34,24 @@ <item>12</item> <item>24</item> </string-array> + <string-array name="episode_cache_size_entries"> - <item>@string/pref_episode_cache_unlimited</item> - <item>1</item> - <item>2</item> <item>5</item> <item>10</item> - <item>20</item> - <item>40</item> - <item>60</item> - <item>80</item> + <item>25</item> + <item>50</item> <item>100</item> + <item>@string/pref_episode_cache_unlimited</item> </string-array> <string-array name="episode_cache_size_values"> - <item>-1</item> - <item>1</item> - <item>2</item> <item>5</item> <item>10</item> - <item>20</item> - <item>40</item> - <item>60</item> - <item>80</item> + <item>25</item> + <item>50</item> <item>100</item> + <item>-1</item> </string-array> + <string-array name="playback_speed_values"> <item>0.5</item> <item>0.6</item> @@ -137,6 +126,28 @@ <item>@string/add_feed_label</item> </string-array> + <string-array name="nav_drawer_feed_order_options"> + <item>@string/drawer_feed_order_unplayed_episodes</item> + <item>@string/drawer_feed_order_alphabetical</item> + </string-array> + <string-array name="nav_drawer_feed_order_values"> + <item>0</item> + <item>1</item> + </string-array> + + <string-array name="nav_drawer_feed_counter_options"> + <item>@string/drawer_feed_counter_new_unplayed</item> + <item>@string/drawer_feed_counter_new</item> + <item>@string/drawer_feed_counter_unplayed</item> + <item>@string/drawer_feed_counter_none</item> + </string-array> + <string-array name="nav_drawer_feed_counter_values"> + <item>0</item> + <item>1</item> + <item>2</item> + <item>3</item> + </string-array> + <string-array name="episode_hide_options"> <item>@string/hide_unplayed_episodes_label</item> <item>@string/hide_paused_episodes_label</item> @@ -157,4 +168,20 @@ <item>not_downloaded</item> </string-array> + <string-array name="image_cache_size_options"> + <item>20 MiB</item> + <item>50 MiB</item> + <item>100 MiB</item> + <item>250 MiB</item> + <item>500 MiB</item> + </string-array> + + <string-array name="image_cache_size_values"> + <item>20</item> + <item>50</item> + <item>100</item> + <item>250</item> + <item>500</item> + </string-array> + </resources> diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml index e558a5c4e..bc0521dcd 100644 --- a/core/src/main/res/values/colors.xml +++ b/core/src/main/res/values/colors.xml @@ -3,6 +3,7 @@ <color name="white">#FFFFFF</color> <color name="gray">#808080</color> + <color name="light_gray">#bfbfbf</color> <color name="black">#000000</color> <color name="bright_blue">#33B5E5</color> <color name="ics_gray">#858585</color> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 3cedfb8e5..00bc077e8 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -7,46 +7,54 @@ <!-- Activitiy and fragment titles --> <string name="app_name">AntennaPod</string> <string name="feeds_label">Feeds</string> - <string name="add_feed_label">Add podcast</string> + <string name="add_feed_label">Add Podcast</string> <string name="podcasts_label">PODCASTS</string> <string name="episodes_label">EPISODES</string> - <string name="new_episodes_label">New episodes</string> - <string name="all_episodes_label">All episodes</string> + <string name="new_episodes_label">New Episodes</string> + <string name="all_episodes_label">All Episodes</string> <string name="new_label">New</string> - <string name="waiting_list_label">Waiting list</string> + <string name="waiting_list_label">Waiting List</string> <string name="settings_label">Settings</string> - <string name="add_new_feed_label">Add podcast</string> + <string name="add_new_feed_label">Add Podcast</string> <string name="downloads_label">Downloads</string> <string name="downloads_running_label">Running</string> <string name="downloads_completed_label">Completed</string> <string name="downloads_log_label">Log</string> - <string name="cancel_download_label">Cancel Download</string> - <string name="playback_history_label">Playback history</string> + <string name="cancel_download_label">Cancel\nDownload</string> + <string name="playback_history_label">Playback History</string> <string name="gpodnet_main_label">gpodder.net</string> - <string name="gpodnet_auth_label">gpodder.net login</string> + <string name="gpodnet_auth_label">gpodder.net Login</string> <!-- New episodes fragment --> <string name="recently_published_episodes_label">Recently published</string> - <string name="episode_filter_label">Show only new episodes</string> + <string name="episode_filter_label">Show only new Episodes</string> <!-- Main activity --> <string name="drawer_open">Open menu</string> <string name="drawer_close">Close menu</string> <string name="drawer_preferences">Drawer Preferences</string> + <string name="drawer_feed_order_unplayed_episodes">Sort by counter</string> + <string name="drawer_feed_order_alphabetical">Sort alphabetically</string> + <string name="drawer_feed_counter_new_unplayed">Number of new and unplayed episodes</string> + <string name="drawer_feed_counter_new">Number of new episodes</string> + <string name="drawer_feed_counter_unplayed">Number of unplayed episodes</string> + <string name="drawer_feed_counter_none">None</string> <!-- Webview actions --> - <string name="open_in_browser_label">Open in browser</string> + <string name="open_in_browser_label">Open in Browser</string> <string name="copy_url_label">Copy URL</string> <string name="share_url_label">Share URL</string> - <string name="copied_url_msg">Copied URL to clipboard.</string> - <string name="go_to_position_label">Go to this position</string> + <string name="copied_url_msg">Copied URL to Clipboard</string> + <string name="go_to_position_label">Go to this Position</string> <!-- Playback history --> - <string name="clear_history_label">Clear history</string> + <string name="clear_history_label">Clear History</string> <!-- Other --> <string name="confirm_label">Confirm</string> <string name="cancel_label">Cancel</string> + <string name="yes">Yes</string> + <string name="no">No</string> <string name="author_label">Author</string> <string name="language_label">Language</string> <string name="url_label">URL</string> @@ -59,7 +67,7 @@ <string name="chapters_label">Chapters</string> <string name="shownotes_label">Shownotes</string> <string name="description_label">Description</string> - <string name="most_recent_prefix">Most Recent Episode:\u0020</string> + <string name="most_recent_prefix">Most recent episode:\u0020</string> <string name="episodes_suffix">\u0020episodes</string> <string name="length_prefix">Length:\u0020</string> <string name="size_prefix">Size:\u0020</string> @@ -69,29 +77,41 @@ <string name="close_label">Close</string> <string name="retry_label">Retry</string> <string name="auto_download_label">Include in auto downloads</string> + <string name="auto_download_apply_to_items_title">Apply to Previous Episodes</string> + <string name="auto_download_apply_to_items_message">The new <i>Auto Download</i> setting will automatically be applied to new episodes.\nDo you also want to apply it to previous episodes?</string> + <string name="auto_delete_label">Auto Delete Episode\n(override global default)</string> <string name="parallel_downloads_suffix">\u0020parallel downloads</string> + <string name="feed_auto_download_global">Global</string> + <string name="feed_auto_download_always">Always</string> + <string name="feed_auto_download_never">Never</string> <!-- 'Add Feed' Activity labels --> <string name="feedurl_label">Feed URL</string> <string name="etxtFeedurlHint">www.example.com/feed</string> <string name="txtvfeedurl_label">Add Podcast by URL</string> - <string name="podcastdirectories_label">Find podcast in directory</string> + <string name="podcastdirectories_label">Find Podcast in Directory</string> <string name="podcastdirectories_descr">You can search for new podcasts by name, category or popularity in the gpodder.net directory, or search the iTunes store.</string> <string name="browse_gpoddernet_label">Browse gpodder.net</string> <!-- Actions on feeds --> <string name="mark_all_read_label">Mark all as played</string> - <string name="mark_all_read_msg">Marked all episodes as played</string> + <string name="mark_all_read_msg">Marked all Episodes as played</string> <string name="mark_all_read_confirmation_msg">Please confirm that you want to mark all episodes as being played.</string> <string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this feed as being played.</string> + <string name="mark_all_seen_label">Mark all as seen</string> <string name="show_info_label">Show information</string> - <string name="remove_feed_label">Remove podcast</string> - <string name="share_link_label">Share website link</string> - <string name="share_source_label">Share feed link</string> + <string name="remove_feed_label">Remove Podcast</string> + <string name="share_label">Share...</string> + <string name="share_link_label">Share Link</string> + <string name="share_link_with_position_label">Share Link with Position</string> + <string name="share_feed_url_label">Share Feed URL</string> + <string name="share_item_url_label">Share Episode URL</string> + <string name="share_item_url_with_position_label">Share Episode URL with Position</string> <string name="feed_delete_confirmation_msg">Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded.</string> - <string name="feed_remover_msg">Removing feed</string> - <string name="load_complete_feed">Refresh complete feed</string> - <string name="hide_episodes_title">Hide episodes</string> + <string name="feed_remover_msg">Removing Feed</string> + <string name="load_complete_feed">Refresh complete Feed</string> + <string name="hide_episodes_title">Hide Episodes</string> + <string name="episode_actions">Apply actions</string> <string name="hide_unplayed_episodes_label">Unplayed</string> <string name="hide_paused_episodes_label">Paused</string> <string name="hide_played_episodes_label">Played</string> @@ -100,7 +120,7 @@ <string name="hide_downloaded_episodes_label">Downloaded</string> <string name="hide_not_downloaded_episodes_label">Not downloaded</string> <string name="filtered_label">Filtered</string> - <string name="refresh_failed_msg">{fa-exclamation-circle} Last refresh failed</string> + <string name="refresh_failed_msg">{fa-exclamation-circle} Last Refresh failed</string> <!-- actions on feeditems --> <string name="download_label">Download</string> @@ -109,10 +129,10 @@ <string name="stop_label">Stop</string> <string name="stream_label">Stream</string> <string name="remove_label">Remove</string> - <string name="remove_episode_lable">Remove episode</string> + <string name="remove_episode_lable">Remove Episode</string> <string name="mark_read_label">Mark as played</string> - <string name="mark_unread_label">Mark as unplayed</string> <string name="marked_as_read_label">Marked as played</string> + <string name="mark_unread_label">Mark as unplayed</string> <string name="add_to_queue_label">Add to Queue</string> <string name="added_to_queue_label">Added to Queue</string> <string name="remove_from_queue_label">Remove from Queue</string> @@ -121,39 +141,39 @@ <string name="enqueue_all_new">Enqueue all</string> <string name="download_all">Download all</string> <string name="skip_episode_label">Skip episode</string> - <string name="activate_auto_download">Activate auto download</string> - <string name="deactivate_auto_download">Deactivate auto download</string> - <string name="reset_position">Reset playback position</string> + <string name="activate_auto_download">Activate Auto Download</string> + <string name="deactivate_auto_download">Deactivate Auto Download</string> + <string name="reset_position">Reset Playback Position</string> <!-- Download messages and labels --> <string name="download_successful">successful</string> <string name="download_failed">failed</string> <string name="download_pending">Download pending</string> <string name="download_running">Download running</string> - <string name="download_error_device_not_found">Storage device not found</string> - <string name="download_error_insufficient_space">Insufficient space</string> - <string name="download_error_file_error">File error</string> + <string name="download_error_device_not_found">Storage Device not found</string> + <string name="download_error_insufficient_space">Insufficient Space</string> + <string name="download_error_file_error">File Error</string> <string name="download_error_http_data_error">HTTP Data Error</string> <string name="download_error_error_unknown">Unknown Error</string> <string name="download_error_parser_exception">Parser Exception</string> - <string name="download_error_unsupported_type">Unsupported Feed type</string> - <string name="download_error_connection_error">Connection error</string> - <string name="download_error_unknown_host">Unknown host</string> - <string name="download_error_unauthorized">Authentication error</string> + <string name="download_error_unsupported_type">Unsupported Feed Type</string> + <string name="download_error_connection_error">Connection Error</string> + <string name="download_error_unknown_host">Unknown Host</string> + <string name="download_error_unauthorized">Authentication Error</string> <string name="cancel_all_downloads_label">Cancel all downloads</string> <string name="download_canceled_msg">Download canceled</string> <string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string> <string name="download_report_title">Downloads completed with error(s)</string> - <string name="download_report_content_title">Download report</string> + <string name="download_report_content_title">Download Report</string> <string name="download_error_malformed_url">Malformed URL</string> <string name="download_error_io_error">IO Error</string> - <string name="download_error_request_error">Request error</string> - <string name="download_error_db_access">Database access error</string> + <string name="download_error_request_error">Request Error</string> + <string name="download_error_db_access">Database Access Error</string> <string name="downloads_left">\u0020Downloads left</string> <string name="downloads_processing">Processing downloads</string> <string name="download_notification_title">Downloading podcast data</string> <string name="download_report_content">%1$d downloads succeeded, %2$d failed</string> - <string name="download_log_title_unknown">Unknown title</string> + <string name="download_log_title_unknown">Unknown Title</string> <string name="download_type_feed">Feed</string> <string name="download_type_media">Media file</string> <string name="download_type_image">Image</string> @@ -181,9 +201,9 @@ <string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string> <!-- Queue operations --> - <string name="lock_queue">Lock queue</string> - <string name="unlock_queue">Unlock queue</string> - <string name="clear_queue_label">Clear queue</string> + <string name="lock_queue">Lock Queue</string> + <string name="unlock_queue">Unlock Queue</string> + <string name="clear_queue_label">Clear Queue</string> <string name="undo">Undo</string> <string name="removed_from_queue">Item removed</string> <string name="move_to_top_label">Move to top</string> @@ -249,38 +269,48 @@ <string name="pref_smart_mark_as_played_title">Smart mark as played</string> <string name="playback_pref">Playback</string> <string name="network_pref">Network</string> - <string name="pref_autoUpdateIntervall_title">Update interval</string> - <string name="pref_autoUpdateIntervall_sum">Specify an interval in which the feeds are refreshed automatically or disable it</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_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> + <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string> <string name="pref_downloadMediaOnWifiOnly_sum">Download media files only over WiFi</string> - <string name="pref_followQueue_title">Continuous playback</string> + <string name="pref_followQueue_title">Continuous Playback</string> <string name="pref_downloadMediaOnWifiOnly_title">WiFi media download</string> - <string name="pref_pauseOnHeadsetDisconnect_title">Headphones disconnect</string> - <string name="pref_unpauseOnHeadsetReconnect_title">Headphones reconnect</string> - <string name="pref_mobileUpdate_title">Mobile updates</string> + <string name="pref_pauseOnHeadsetDisconnect_title">Headphones Disconnect</string> + <string name="pref_unpauseOnHeadsetReconnect_title">Headphones Reconnect</string> + <string name="pref_mobileUpdate_title">Mobile Updates</string> <string name="pref_mobileUpdate_sum">Allow updates over the mobile data connection</string> <string name="refreshing_label">Refreshing</string> <string name="flattr_settings_label">Flattr settings</string> <string name="pref_flattr_auth_title">Flattr sign-in</string> <string name="pref_flattr_auth_sum">Sign in to your flattr account to flattr things directly from the app.</string> - <string name="pref_flattr_this_app_title">Flattr this app</string> + <string name="pref_flattr_this_app_title">Flattr this App</string> <string name="pref_flattr_this_app_sum">Support the development of AntennaPod by flattring it. Thanks!</string> <string name="pref_revokeAccess_title">Revoke access</string> <string name="pref_revokeAccess_sum">Revoke the access permission to your flattr account for this app.</string> <string name="pref_auto_flattr_title">Automatic Flattr</string> <string name="pref_auto_flattr_sum">Configure automatic flattring</string> <string name="user_interface_label">User Interface</string> - <string name="pref_set_theme_title">Select theme</string> - <string name="pref_nav_drawer_items_title">Change navigation drawer</string> + <string name="pref_set_theme_title">Select Theme</string> + <string name="pref_nav_drawer_title">Customize Navigation Drawer</string> + <string name="pref_nav_drawer_sum">Customize the appearance of the navigation drawer.</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> + <string name="pref_nav_drawer_feed_order_title">Set Subscription Order</string> + <string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string> + <string name="pref_nav_drawer_feed_counter_title">Set Subscription Counter</string> + <string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter</string> <string name="pref_set_theme_sum">Change the appearance of AntennaPod.</string> - <string name="pref_automatic_download_title">Automatic download</string> + <string name="pref_automatic_download_title">Automatic Download</string> <string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string> <string name="pref_autodl_wifi_filter_title">Enable Wi-Fi filter</string> <string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string> <string name="pref_automatic_download_on_battery_title">Download when not charging</string> <string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string> - <string name="pref_parallel_downloads_title">Parallel downloads</string> - <string name="pref_episode_cache_title">Episode cache</string> + <string name="pref_parallel_downloads_title">Parallel Downloads</string> + <string name="pref_episode_cache_title">Episode Cache</string> <string name="pref_theme_title_light">Light</string> <string name="pref_theme_title_dark">Dark</string> <string name="pref_episode_cache_unlimited">Unlimited</string> @@ -301,13 +331,16 @@ <string name="pref_gpodnet_sethostname_use_default_host">Use default host</string> <string name="pref_expandNotify_title">Expand Notification</string> <string name="pref_expandNotify_sum">Always expand the notification to show playback buttons.</string> - <string name="pref_persistNotify_title">Persistent playback controls</string> + <string name="pref_persistNotify_title">Persistent Playback Controls</string> <string name="pref_persistNotify_sum">Keep notification and lockscreen controls when playback is paused.</string> + <string name="pref_showDownloadReport_title">Show Download Report</string> + <string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string> <string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string> <string name="pref_queueAddToFront_sum">Add new episodes to the front of the queue.</string> - <string name="pref_queueAddToFront_title">Enqueue at front.</string> + <string name="pref_queueAddToFront_title">Enqueue at Front</string> <string name="pref_smart_mark_as_played_disabled">Disabled</string> - + <string name="pref_image_cache_size_title">Image Cache Size</string> + <string name="pref_image_cache_size_sum">Size of the disk cache for images.</string> <!-- Auto-Flattr dialog --> <string name="auto_flattr_enable">Enable automatic flattring</string> @@ -328,19 +361,20 @@ <string name="opml_import_explanation_1">Choose a specific file path from the local filesystem.</string> <string name="opml_import_explanation_2">Use an external applications like Dropbox, Google Drive or your favourite file manager to open an OPML file.</string> <string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string> <string name="start_import_label">Start import</string> - <string name="opml_import_label">OPML import</string> + <string name="opml_import_label">OPML Import</string> <string name="opml_directory_error">ERROR!</string> <string name="reading_opml_label">Reading OPML file</string> <string name="opml_reader_error">An error has occurred while reading the opml document:</string> <string name="opml_import_error_dir_empty">The import directory is empty.</string> <string name="select_all_label">Select all</string> <string name="deselect_all_label">Deselect all</string> + <string name="select_options_label">Select ...</string> <string name="choose_file_from_filesystem">From local filesystem</string> <string name="choose_file_from_external_application">Use external application</string> <string name="opml_export_label">OPML export</string> <string name="exporting_label">Exporting...</string> <string name="export_error_label">Export error</string> - <string name="opml_export_success_title">OPML export successful.</string> + <string name="opml_export_success_title">OPML Export successful.</string> <string name="opml_export_success_sum">The .opml file was written to:\u0020</string> <!-- Sleep timer --> @@ -350,9 +384,21 @@ <string name="sleep_timer_label">Sleep timer</string> <string name="time_left_label">Time left:\u0020</string> <string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string> - <string name="time_unit_seconds">seconds</string> - <string name="time_unit_minutes">minutes</string> - <string name="time_unit_hours">hours</string> + <string name="time_seconds">seconds</string> + <string name="time_minutes">minutes</string> + <string name="time_hours">hours</string> + <plurals name="time_seconds_quantified"> + <item quantity="one">1 second</item> + <item quantity="other">%d seconds</item> + </plurals> + <plurals name="time_minutes_quantified"> + <item quantity="one">1 minute</item> + <item quantity="other">%d minutes</item> + </plurals> + <plurals name="time_hours_quantified"> + <item quantity="one">1 hour</item> + <item quantity="other">%d hours</item> + </plurals> <!-- gpodder.net --> <string name="gpodnet_taglist_header">CATEGORIES</string> @@ -388,7 +434,7 @@ <!-- Directory chooser --> <string name="selected_folder_label">Selected folder:</string> <string name="create_folder_label">Create folder</string> - <string name="choose_data_directory">Choose data folder</string> + <string name="choose_data_directory">Choose Data Folder</string> <string name="create_folder_msg">Create new folder with name "%1$s"?</string> <string name="create_folder_success">Created new folder</string> <string name="create_folder_error_no_write_access">Cannot write to this folder</string> @@ -398,9 +444,10 @@ <string name="folder_not_empty_dialog_msg">The folder you have selected is not empty. Media downloads and other files will be placed directly in this folder. Continue anyway?</string> <string name="set_to_default_folder">Choose default folder</string> <string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string> - <string name="pref_pausePlaybackForFocusLoss_title">Pause for interruptions</string> + <string name="pref_pausePlaybackForFocusLoss_title">Pause for Interruptions</string> <string name="pref_resumeAfterCall_sum">Resume playback after a phone call completes</string> - <string name="pref_resumeAfterCall_title">Resume after call</string> + <string name="pref_resumeAfterCall_title">Resume after Call</string> + <string name="pref_restart_required">AntennaPod has to be restarted for this change to take effect.</string> <!-- Online feed view --> <string name="subscribe_label">Subscribe</string> @@ -431,8 +478,33 @@ <string name="authentication_label">Authentication</string> <string name="authentication_descr">Change your username and password for this podcast and its episodes.</string> + + <!-- Progress information --> + <string name="progress_upgrading_database">Upgrading the database</string> + <!-- AntennaPodSP --> <string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string> <string name="search_itunes_label">Search iTunes</string> + + <string name="select_label"><b>Select ...</b></string> + <string name="all_label">All</string> + <string name="selected_all_label">Selected all Episodes</string> + <string name="none_label">None</string> + <string name="deselected_all_label">Deselected all Episodes</string> + <string name="played_label">Played</string> + <string name="selected_played_label">Selected played Episodes</string> + <string name="unplayed_label">Unplayed</string> + <string name="selected_unplayed_label">Selected unplayed Episodes</string> + <string name="downloaded_label">Downloaded</string> + <string name="selected_downloaded_label">Selected downloaded Episodes</string> + <string name="not_downloaded_label">Not downloaded</string> + <string name="selected_not_downloaded_label">Selected not downloaded Episodes</string> + <string name="sort_title"><b>Sort by ...</b></string> + <string name="sort_title_a_z">Title (A \u2192 Z)</string> + <string name="sort_title_z_a">Title (Z \u2192 A)</string> + <string name="sort_date_new_old">Date (New \u2192 Old)</string> + <string name="sort_date_old_new">Date (Old \u2192 New)</string> + <string name="sort_duration_short_long">Duration (Short \u2192 Long)</string> + <string name="sort_duration_long_short">Duration (Long \u2192 Short)</string> </resources> diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index 8619869c8..85c908f64 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -263,7 +263,9 @@ <style name="BigBlurryBackground"> <item name="android:scaleType">centerCrop</item> - <item name="android:tint">@color/image_readability_tint</item> + <!-- <item name="android:tint">@color/image_readability_tint</item> --> + <!-- Reactivate when Glide's tinting has been fixed for Android 5.x + Remove color filter from ItemlistFragment --> </style> |