summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tx/config2
-rw-r--r--CHANGELOG.md4
-rw-r--r--CONTRIBUTING.md24
-rw-r--r--app/build.gradle2
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java646
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java45
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java78
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java79
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java89
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java118
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java119
-rw-r--r--app/src/main/res/layout-v14/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout-v14/download_authentication_activity.xml2
-rw-r--r--app/src/main/res/layout-v14/time_dialog.xml66
-rw-r--r--app/src/main/res/layout/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout/download_authentication_activity.xml2
-rw-r--r--app/src/main/res/layout/downloadlog_item.xml3
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml2
-rw-r--r--app/src/main/res/layout/feedinfo.xml2
-rw-r--r--app/src/main/res/layout/feeditemlist_item.xml9
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml14
-rw-r--r--app/src/main/res/layout/queue_listitem.xml13
-rw-r--r--app/src/main/res/layout/time_dialog.xml58
-rw-r--r--app/src/main/res/xml/preferences.xml16
-rw-r--r--build.gradle1
-rw-r--r--changelog/ko_KR.md327
-rw-r--r--changelog/ru_RU.md10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java191
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java53
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java123
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java193
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java177
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java65
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LongList.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java2
-rw-r--r--core/src/main/res/values-nb/strings.xml502
-rw-r--r--core/src/main/res/values-no/strings.xml31
-rw-r--r--core/src/main/res/values-v16/styles.xml2
-rw-r--r--core/src/main/res/values/colors.xml8
-rw-r--r--core/src/main/res/values/strings.xml6
-rw-r--r--core/src/main/res/values/styles.xml68
-rw-r--r--description/it_IT.txt22
-rw-r--r--description/ko_KR.txt42
-rw-r--r--description/uk_UA.txt66
-rw-r--r--gradlew.bat180
84 files changed, 2419 insertions, 1702 deletions
diff --git a/.tx/config b/.tx/config
index c0d81ea94..1a452f942 100644
--- a/.tx/config
+++ b/.tx/config
@@ -18,7 +18,7 @@ trans.it_IT = core/src/main/res/values-it-rIT/strings.xml
trans.ja = core/src/main/res/values-ja/strings.xml
trans.ko = core/src/main/res/values-ko/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml
-#trans.no = core/src/main/res/values-no/strings.xml
+trans.nb_NO = core/src/main/res/values-nb/strings.xml
trans.pl_PL = core/src/main/res/values-pl-rPL/strings.xml
trans.pt = core/src/main/res/values-pt/strings.xml
trans.pt_BR = core/src/main/res/values-pt-rBR/strings.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86cd0c6d1..f07b7f7e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
Change Log
==========
+Version 1.4.0.12
+----------------
+* Fix for crash on Huawei devices (media buttons may not work)
+
Version 1.4
-----------
* BLUETOOTH PERMISSION: Needed to be able to resume playback when a Bluetooth device reconnects with your phone
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9dfd9ecfe..5841f1c79 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,6 +7,30 @@ How to report a bug
- If the bug only seems to occur with a certain podcast, make sure to include the URL of that podcast.
- If possible, add instructions on how to reproduce the bug.
- If possible, add a logfile to your post. This is especially useful if the bug makes the application crash. You can create logfiles with an app like `aLogcat`. Just launch `alogcat`, then start AntennaPod and reproduce the bug. aLogcat should now display information about the bug when you start it.
+- Please use the following **template**:
+
+**Android version**: 5.x [Especially mention custom roms!]
+
+**Devide model**:
+
+**Expected behaviour**:
+
+**Current behaviour**:
+
+**First occured**: Version 1.x / about x days/weeks ago
+
+**Steps to reproduce**:
+
+1. Do this
+1. Do that
+
+**Environment**: Settings you have changed, e.g. Auto Download. "Unusual" devices you use, e.g. Bluetooth headphones. Do you still use Prestissimo?
+
+**Stacktrace/Logcat**: [if available]
+```
+...
+```
+
How to submit a feature request
-------------------------------
diff --git a/app/build.gradle b/app/build.gradle
index 3368af0d6..0e6c9865b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,6 +19,7 @@ dependencies {
compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"
}
+
compile "commons-io:commons-io:$commonsioVersion"
compile "org.jsoup:jsoup:$jsoupVersion"
compile "com.github.bumptech.glide:glide:$glideVersion"
@@ -27,6 +28,7 @@ dependencies {
compile "com.squareup.okio:okio:$okioVersion"
compile "de.greenrobot:eventbus:$eventbusVersion"
compile "io.reactivex:rxandroid:$rxAndroidVersion"
+ compile "io.reactivex:rxjava:$rxJavaVersion"
compile "com.joanzapata.iconify:android-iconify-fontawesome:2.1.0"
compile "com.afollestad:material-dialogs:0.7.8.1"
compile "com.yqritc:recyclerview-flexibledivider:1.2.6"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 636e1e64c..4efd5f5b6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="1040008"
- android:versionName="1.4.0.8">
+ android:versionCode="1040012"
+ android:versionName="1.4.0.12">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index b87870c69..4a43dc3c6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -772,7 +772,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private DBReader.NavDrawerData navDrawerData;
private void loadData() {
- subscription = Observable.defer(() -> Observable.just(DBReader.getNavDrawerData()))
+ subscription = Observable.fromCallable(() -> DBReader.getNavDrawerData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
index 559fa0574..25dc64232 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -1,370 +1,336 @@
package de.danoeh.antennapod.activity;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
-import android.widget.AdapterView.OnItemClickListener;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
/**
* Let's the user choose a directory on the storage device. The selected folder
* will be sent back to the starting activity as an activity result.
*/
public class DirectoryChooserActivity extends ActionBarActivity {
- private static final String TAG = "DirectoryChooserActivity";
-
- private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
-
- public static final String RESULT_SELECTED_DIR = "selected_dir";
- public static final int RESULT_CODE_DIR_SELECTED = 1;
-
- private Button butConfirm;
- private Button butCancel;
- private ImageButton butNavUp;
- private TextView txtvSelectedFolder;
- private ListView listDirectories;
-
- private ArrayAdapter<String> listDirectoriesAdapter;
- private ArrayList<String> filenames;
- /** The directory that is currently being shown. */
- private File selectedDir;
- private File[] filesInDir;
-
- private FileObserver fileObserver;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.directory_chooser);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
- butNavUp = (ImageButton) findViewById(R.id.butNavUp);
- txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
- listDirectories = (ListView) findViewById(R.id.directory_list);
-
- butConfirm.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (isValidFile(selectedDir)) {
- if (selectedDir.list().length == 0) {
- returnSelectedFolder();
- } else {
- showNonEmptyDirectoryWarning();
- }
- }
- }
-
- private void showNonEmptyDirectoryWarning() {
- AlertDialog.Builder adb = new AlertDialog.Builder(
- DirectoryChooserActivity.this);
- adb.setTitle(R.string.folder_not_empty_dialog_title);
- adb.setMessage(R.string.folder_not_empty_dialog_msg);
- adb.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
- });
- adb.setPositiveButton(R.string.confirm_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- returnSelectedFolder();
- }
- });
- adb.create().show();
- }
- });
-
- butCancel.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- setResult(Activity.RESULT_CANCELED);
- finish();
- }
- });
-
- listDirectories.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> adapter, View view,
- int position, long id) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Selected index: " + position);
- if (filesInDir != null && position >= 0
- && position < filesInDir.length) {
- changeDirectory(filesInDir[position]);
- }
- }
- });
-
- butNavUp.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- File parent = null;
- if (selectedDir != null
- && (parent = selectedDir.getParentFile()) != null) {
- changeDirectory(parent);
- }
- }
- });
-
- filenames = new ArrayList<String>();
- listDirectoriesAdapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_1, filenames);
- listDirectories.setAdapter(listDirectoriesAdapter);
- changeDirectory(Environment.getExternalStorageDirectory());
- }
-
- /**
- * Finishes the activity and returns the selected folder as a result. The
- * selected folder can also be null.
- */
- private void returnSelectedFolder() {
- if (selectedDir != null && BuildConfig.DEBUG)
- Log.d(TAG, "Returning " + selectedDir.getAbsolutePath()
- + " as result");
- Intent resultData = new Intent();
- if (selectedDir != null) {
- resultData.putExtra(RESULT_SELECTED_DIR,
- selectedDir.getAbsolutePath());
- }
- setResult(RESULT_CODE_DIR_SELECTED, resultData);
- finish();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (fileObserver != null) {
- fileObserver.stopWatching();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (fileObserver != null) {
- fileObserver.startWatching();
- }
- }
-
- /**
- * Change the directory that is currently being displayed.
- *
- * @param dir
- * The file the activity should switch to. This File must be
- * non-null and a directory, otherwise the displayed directory
- * will not be changed
- */
- private void changeDirectory(File dir) {
- if (dir != null && dir.isDirectory()) {
- File[] contents = dir.listFiles();
- if (contents != null) {
- int numDirectories = 0;
- for (File f : contents) {
- if (f.isDirectory()) {
- numDirectories++;
- }
- }
- filesInDir = new File[numDirectories];
- filenames.clear();
- for (int i = 0, counter = 0; i < numDirectories; counter++) {
- if (contents[counter].isDirectory()) {
- filesInDir[i] = contents[counter];
- filenames.add(contents[counter].getName());
- i++;
- }
- }
- Arrays.sort(filesInDir);
- Collections.sort(filenames);
- selectedDir = dir;
- txtvSelectedFolder.setText(dir.getAbsolutePath());
- listDirectoriesAdapter.notifyDataSetChanged();
- fileObserver = createFileObserver(dir.getAbsolutePath());
- fileObserver.startWatching();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Changed directory to " + dir.getAbsolutePath());
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Could not change folder: contents of dir were null");
- }
- } else {
- if (dir == null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not change folder: dir was null");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not change folder: dir is no directory");
- }
- }
- refreshButtonState();
- }
-
- /**
- * Changes the state of the buttons depending on the currently selected file
- * or folder.
- */
- private void refreshButtonState() {
- if (selectedDir != null) {
- butConfirm.setEnabled(isValidFile(selectedDir));
- supportInvalidateOptionsMenu();
- }
- }
-
- /** Refresh the contents of the directory that is currently shown. */
- private void refreshDirectory() {
- if (selectedDir != null) {
- changeDirectory(selectedDir);
- }
- }
-
- /** Sets up a FileObserver to watch the current directory. */
- private FileObserver createFileObserver(String path) {
- return new FileObserver(path, FileObserver.CREATE | FileObserver.DELETE
- | FileObserver.MOVED_FROM | FileObserver.MOVED_TO) {
-
- @Override
- public void onEvent(int event, String path) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "FileObserver received event " + event);
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- refreshDirectory();
- }
- });
- }
- };
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
+ private static final String TAG = "DirectoryChooserActivit";
+
+ private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
+
+ public static final String RESULT_SELECTED_DIR = "selected_dir";
+ public static final int RESULT_CODE_DIR_SELECTED = 1;
+
+ private Button butConfirm;
+ private Button butCancel;
+ private ImageButton butNavUp;
+ private TextView txtvSelectedFolder;
+ private ListView listDirectories;
+
+ private ArrayAdapter<String> listDirectoriesAdapter;
+ private ArrayList<String> filenames;
+ /** The directory that is currently being shown. */
+ private File selectedDir;
+ private File[] filesInDir;
+
+ private FileObserver fileObserver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setContentView(R.layout.directory_chooser);
+ butConfirm = (Button) findViewById(R.id.butConfirm);
+ butCancel = (Button) findViewById(R.id.butCancel);
+ butNavUp = (ImageButton) findViewById(R.id.butNavUp);
+ txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
+ listDirectories = (ListView) findViewById(R.id.directory_list);
+
+ butConfirm.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isValidFile(selectedDir)) {
+ if (selectedDir.list().length == 0) {
+ returnSelectedFolder();
+ } else {
+ showNonEmptyDirectoryWarning();
+ }
+ }
+ }
+
+ private void showNonEmptyDirectoryWarning() {
+ AlertDialog.Builder adb = new AlertDialog.Builder(
+ DirectoryChooserActivity.this);
+ adb.setTitle(R.string.folder_not_empty_dialog_title);
+ adb.setMessage(R.string.folder_not_empty_dialog_msg);
+ adb.setNegativeButton(R.string.cancel_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ });
+ adb.setPositiveButton(R.string.confirm_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ returnSelectedFolder();
+ });
+ adb.create().show();
+ }
+ });
+
+ butCancel.setOnClickListener(v -> {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ });
+
+ listDirectories.setOnItemClickListener((adapter, view, position, id) -> {
+ Log.d(TAG, "Selected index: " + position);
+ if (filesInDir != null && position >= 0
+ && position < filesInDir.length) {
+ changeDirectory(filesInDir[position]);
+ }
+ });
+
+ butNavUp.setOnClickListener(v -> {
+ File parent = null;
+ if (selectedDir != null
+ && (parent = selectedDir.getParentFile()) != null) {
+ changeDirectory(parent);
+ }
+ });
+
+ filenames = new ArrayList<>();
+ listDirectoriesAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, filenames);
+ listDirectories.setAdapter(listDirectoriesAdapter);
+ changeDirectory(Environment.getExternalStorageDirectory());
+ }
+
+ /**
+ * Finishes the activity and returns the selected folder as a result. The
+ * selected folder can also be null.
+ */
+ private void returnSelectedFolder() {
+ if (selectedDir != null && BuildConfig.DEBUG)
+ Log.d(TAG, "Returning " + selectedDir.getAbsolutePath()
+ + " as result");
+ Intent resultData = new Intent();
+ if (selectedDir != null) {
+ resultData.putExtra(RESULT_SELECTED_DIR,
+ selectedDir.getAbsolutePath());
+ }
+ setResult(Activity.RESULT_OK, resultData);
+ finish();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (fileObserver != null) {
+ fileObserver.stopWatching();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (fileObserver != null) {
+ fileObserver.startWatching();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ listDirectoriesAdapter = null;
+ fileObserver = null;
+ }
+
+ /**
+ * Change the directory that is currently being displayed.
+ *
+ * @param dir
+ * The file the activity should switch to. This File must be
+ * non-null and a directory, otherwise the displayed directory
+ * will not be changed
+ */
+ private void changeDirectory(File dir) {
+ if (dir != null && dir.isDirectory()) {
+ File[] contents = dir.listFiles();
+ if (contents != null) {
+ int numDirectories = 0;
+ for (File f : contents) {
+ if (f.isDirectory()) {
+ numDirectories++;
+ }
+ }
+ filesInDir = new File[numDirectories];
+ filenames.clear();
+ for (int i = 0, counter = 0; i < numDirectories; counter++) {
+ if (contents[counter].isDirectory()) {
+ filesInDir[i] = contents[counter];
+ filenames.add(contents[counter].getName());
+ i++;
+ }
+ }
+ Arrays.sort(filesInDir);
+ Collections.sort(filenames);
+ selectedDir = dir;
+ txtvSelectedFolder.setText(dir.getAbsolutePath());
+ listDirectoriesAdapter.notifyDataSetChanged();
+ fileObserver = createFileObserver(dir.getAbsolutePath());
+ fileObserver.startWatching();
+ Log.d(TAG, "Changed directory to " + dir.getAbsolutePath());
+ } else {
+ Log.d(TAG, "Could not change folder: contents of dir were null");
+ }
+ } else {
+ if (dir == null) {
+ Log.d(TAG, "Could not change folder: dir was null");
+ } else {
+ Log.d(TAG, "Could not change folder: dir is no directory");
+ }
+ }
+ refreshButtonState();
+ }
+
+ /**
+ * Changes the state of the buttons depending on the currently selected file
+ * or folder.
+ */
+ private void refreshButtonState() {
+ if (selectedDir != null) {
+ butConfirm.setEnabled(isValidFile(selectedDir));
+ supportInvalidateOptionsMenu();
+ }
+ }
+
+ /** Refresh the contents of the directory that is currently shown. */
+ private void refreshDirectory() {
+ if (selectedDir != null) {
+ changeDirectory(selectedDir);
+ }
+ }
+
+ /** Sets up a FileObserver to watch the current directory. */
+ private FileObserver createFileObserver(String path) {
+ return new FileObserver(path, FileObserver.CREATE | FileObserver.DELETE
+ | FileObserver.MOVED_FROM | FileObserver.MOVED_TO) {
+
+ @Override
+ public void onEvent(int event, String path) {
+ Log.d(TAG, "FileObserver received event " + event);
+ runOnUiThread(() -> refreshDirectory());
+ }
+ };
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.new_folder_item)
- .setVisible(isValidFile(selectedDir));
- return true;
- }
+ menu.findItem(R.id.new_folder_item)
+ .setVisible(isValidFile(selectedDir));
+ return true;
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.directory_chooser, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
- return true;
- case R.id.new_folder_item:
- openNewFolderDialog();
- return true;
- case R.id.set_to_default_folder_item:
- selectedDir = null;
- returnSelectedFolder();
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Shows a confirmation dialog that asks the user if he wants to create a
- * new folder.
- */
- private void openNewFolderDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.create_folder_label);
- builder.setMessage(String.format(getString(R.string.create_folder_msg),
- CREATE_DIRECTORY_NAME));
- builder.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- });
- builder.setPositiveButton(R.string.confirm_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- int msg = createFolder();
- Toast t = Toast.makeText(DirectoryChooserActivity.this,
- msg, Toast.LENGTH_SHORT);
- t.show();
- }
- });
- builder.create().show();
- }
-
- /**
- * Creates a new folder in the current directory with the name
- * CREATE_DIRECTORY_NAME.
- */
- private int createFolder() {
- if (selectedDir == null) {
- return R.string.create_folder_error;
- } else if (selectedDir.canWrite()) {
- File newDir = new File(selectedDir, CREATE_DIRECTORY_NAME);
- if (!newDir.exists()) {
- boolean result = newDir.mkdir();
- if (result) {
- return R.string.create_folder_success;
- } else {
- return R.string.create_folder_error;
- }
- } else {
- return R.string.create_folder_error_already_exists;
- }
- } else {
- return R.string.create_folder_error_no_write_access;
- }
- }
-
- /** Returns true if the selected file or directory would be valid selection. */
- private boolean isValidFile(File file) {
- return (file != null && file.isDirectory() && file.canRead() && file
- .canWrite());
- }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.directory_chooser, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ case R.id.new_folder_item:
+ openNewFolderDialog();
+ return true;
+ case R.id.set_to_default_folder_item:
+ selectedDir = null;
+ returnSelectedFolder();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Shows a confirmation dialog that asks the user if he wants to create a
+ * new folder.
+ */
+ private void openNewFolderDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.create_folder_label);
+ builder.setMessage(String.format(getString(R.string.create_folder_msg),
+ CREATE_DIRECTORY_NAME));
+ builder.setNegativeButton(R.string.cancel_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ });
+ builder.setPositiveButton(R.string.confirm_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ int msg = createFolder();
+ Toast t = Toast.makeText(DirectoryChooserActivity.this,
+ msg, Toast.LENGTH_SHORT);
+ t.show();
+ });
+ builder.create().show();
+ }
+
+ /**
+ * Creates a new folder in the current directory with the name
+ * CREATE_DIRECTORY_NAME.
+ */
+ private int createFolder() {
+ if (selectedDir == null) {
+ return R.string.create_folder_error;
+ } else if (selectedDir.canWrite()) {
+ File newDir = new File(selectedDir, CREATE_DIRECTORY_NAME);
+ if (!newDir.exists()) {
+ boolean result = newDir.mkdir();
+ if (result) {
+ return R.string.create_folder_success;
+ } else {
+ return R.string.create_folder_error;
+ }
+ } else {
+ return R.string.create_folder_error_already_exists;
+ }
+ } else {
+ return R.string.create_folder_error_no_write_access;
+ }
+ }
+
+ /** Returns true if the selected file or directory would be valid selection. */
+ private boolean isValidFile(File file) {
+ return file != null && file.isDirectory() && file.canRead() && file.canWrite();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 14128bec9..211b895d0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -21,6 +20,7 @@ import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.ContextMenu;
@@ -42,10 +42,9 @@ import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.ProgressEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.event.QueueEvent;
-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.playback.PlaybackService;
@@ -78,9 +77,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private static final String TAG = "MainActivity";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
- | EventDistributor.DOWNLOAD_QUEUED
- | EventDistributor.FEED_LIST_UPDATE
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
| EventDistributor.UNREAD_ITEMS_UPDATE;
public static final String PREF_NAME = "MainActivityPrefs";
@@ -642,7 +639,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
};
private void loadData() {
- subscription = Observable.defer(() -> Observable.just(DBReader.getNavDrawerData()))
+ subscription = Observable.fromCallable(() -> DBReader.getNavDrawerData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index e34b4dc67..c53a5257b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
@@ -8,6 +7,7 @@ import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -19,6 +19,8 @@ import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import com.afollestad.materialdialogs.MaterialDialog;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -291,49 +293,40 @@ public abstract class MediaplayerActivity extends ActionBarActivity
switch (item.getItemId()) {
case R.id.disable_sleeptimer_item:
if (controller.serviceAvailable()) {
- AlertDialog.Builder stDialog = new AlertDialog.Builder(this);
- stDialog.setTitle(R.string.sleep_timer_label);
- stDialog.setMessage(getString(R.string.time_left_label)
+
+ MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
+ stDialog.title(R.string.sleep_timer_label);
+ stDialog.content(getString(R.string.time_left_label)
+ Converter.getDurationStringLong((int) controller
.getSleepTimerTimeLeft()));
- stDialog.setPositiveButton(
- R.string.disable_sleeptimer_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- controller.disableSleepTimer();
- }
- }
- );
- stDialog.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
- }
- );
- stDialog.create().show();
+ stDialog.positiveText(R.string.disable_sleeptimer_label);
+ stDialog.negativeText(R.string.cancel_label);
+ stDialog.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ dialog.dismiss();
+ controller.disableSleepTimer();
+ }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
+ }
+ });
+ stDialog.build().show();
}
break;
case R.id.set_sleeptimer_item:
if (controller.serviceAvailable()) {
- SleepTimerDialog td = new SleepTimerDialog(this, 0, 0) {
+ SleepTimerDialog td = new SleepTimerDialog(this) {
@Override
public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
controller.setSleepTimer(millis, shakeToReset, vibrate);
}
};
- td.show();
-
- break;
-
+ td.createNewDialog().show();
}
+ break;
case R.id.visit_website_item:
Uri uri = Uri.parse(media.getWebsiteLink());
startActivity(new Intent(Intent.ACTION_VIEW, uri));
@@ -500,19 +493,13 @@ public abstract class MediaplayerActivity extends ActionBarActivity
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
builder.setTitle(R.string.pref_fast_forward);
builder.setSingleChoiceItems(choices, checked,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice = values[which];
- }
+ (dialog, which) -> {
+ choice = values[which];
});
builder.setNegativeButton(R.string.cancel_label, null);
- builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setPrefFastForwardSecs(choice);
- txtvFF.setText(String.valueOf(choice));
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setPrefFastForwardSecs(choice);
+ txtvFF.setText(String.valueOf(choice));
});
builder.create().show();
return true;
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index a629b8758..b32389bd9 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -9,6 +8,7 @@ import android.os.Bundle;
import android.os.Looper;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -41,6 +41,7 @@ import java.util.Map;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -63,6 +64,7 @@ import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
@@ -86,7 +88,7 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
// Optional argument: specify a title for the actionbar.
public static final String ARG_TITLE = "title";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE;
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
public static final int RESULT_ERROR = 2;
@@ -105,11 +107,16 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
private Subscription parser;
private Subscription updater;
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ setSubscribeButtonState(feed);
+ }
+
private EventDistributor.EventListener listener = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- updater = Observable.defer(() -> Observable.just(DBReader.getFeedList()))
+ updater = Observable.fromCallable(() -> DBReader.getFeedList())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(feeds -> {
@@ -180,7 +187,7 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
super.onResume();
isPaused = false;
EventDistributor.getInstance().register(listener);
-
+ EventBus.getDefault().register(this);
}
@Override
@@ -188,6 +195,7 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
super.onPause();
isPaused = true;
EventDistributor.getInstance().unregister(listener);
+ EventBus.getDefault().unregister(this);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
index f7e9256c0..46e5f0e8e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import de.danoeh.antennapod.core.preferences.UserPreferences;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index 74cf6af60..1a8f0a67a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -7,14 +7,10 @@ import android.widget.ImageButton;
import org.apache.commons.lang3.Validate;
-import java.lang.ref.WeakReference;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.LongList;
/**
* Utility methods for the action button that is displayed on the right hand side
@@ -51,7 +47,7 @@ public class ActionButtonUtils {
* Sets the displayed bitmap and content description of the given
* action button so that it matches the state of the FeedItem.
*/
- public void configureActionButton(ImageButton butSecondary, FeedItem item) {
+ public void configureActionButton(ImageButton butSecondary, FeedItem item, boolean isInQueue) {
Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
final FeedMedia media = item.getMedia();
@@ -66,9 +62,8 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[1]));
} else {
// item is not downloaded and not being downloaded
- LongList queueIds = DBReader.getQueueIDList();
if(DefaultActionButtonCallback.userAllowedMobileDownloads() ||
- !DefaultActionButtonCallback.userChoseAddToQueue() || queueIds.contains(item.getId())) {
+ !DefaultActionButtonCallback.userChoseAddToQueue() || isInQueue) {
butSecondary.setVisibility(View.VISIBLE);
butSecondary.setImageDrawable(drawables.getDrawable(2));
butSecondary.setContentDescription(context.getString(labels[2]));
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index b35ff0aa9..f120aa1d5 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -1,22 +1,21 @@
package de.danoeh.antennapod.adapter;
-import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -34,7 +33,7 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.NetworkUtils;
@@ -48,27 +47,34 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private static final String TAG = AllEpisodesRecycleAdapter.class.getSimpleName();
- private final Context context;
+ private final WeakReference<MainActivity> mainActivityRef;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private final boolean showOnlyNewEpisodes;
- private final WeakReference<MainActivity> mainActivityRef;
private int position = -1;
- public AllEpisodesRecycleAdapter(Context context,
- MainActivity mainActivity,
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
+
+ public AllEpisodesRecycleAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
ActionButtonCallback actionButtonCallback,
boolean showOnlyNewEpisodes) {
super();
this.mainActivityRef = new WeakReference<>(mainActivity);
- this.context = context;
this.itemAccess = itemAccess;
- this.actionButtonUtils = new ActionButtonUtils(context);
+ this.actionButtonUtils = new ActionButtonUtils(mainActivity);
this.actionButtonCallback = actionButtonCallback;
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
+
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ }
+ normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
}
@Override
@@ -76,6 +82,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.new_episodes_listitem, parent, false);
Holder holder = new Holder(view);
+ holder.container = (FrameLayout) view.findViewById(R.id.container);
holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder);
holder.title = (TextView) view.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) view
@@ -111,7 +118,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
- holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+ holder.pubDate.setText(DateUtils.formatDateTime(mainActivityRef.get(),
+ item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
if (showOnlyNewEpisodes || false == item.isNew()) {
holder.statusUnread.setVisibility(View.INVISIBLE);
} else {
@@ -161,23 +169,29 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.GONE);
}
+ if(media.isCurrentlyPlaying()) {
+ holder.container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.container.setBackgroundColor(normalBackGroundColor);
+ }
} else {
holder.progress.setVisibility(View.GONE);
holder.txtvDuration.setVisibility(View.GONE);
}
- if (itemAccess.isInQueue(item)) {
+ boolean isInQueue = itemAccess.isInQueue(item);
+ if (isInQueue) {
holder.queueStatus.setVisibility(View.VISIBLE);
} else {
holder.queueStatus.setVisibility(View.INVISIBLE);
}
- actionButtonUtils.configureActionButton(holder.butSecondary, item);
+ actionButtonUtils.configureActionButton(holder.butSecondary, item, isInQueue);
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Glide.with(context)
+ Glide.with(mainActivityRef.get())
.load(item.getImageUri())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
@@ -224,7 +238,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
TextView txtvPlaceholder = placeholder.get();
ImageView imgvCover = cover.get();
if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
- Glide.with(context)
+ Glide.with(mainActivityRef.get())
.load(fallbackUri)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
@@ -255,6 +269,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
implements View.OnClickListener,
View.OnCreateContextMenuListener,
ItemTouchHelperViewHolder {
+ FrameLayout container;
TextView placeholder;
TextView title;
TextView pubDate;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
index 22c15949a..cf0532cf1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -138,9 +138,9 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
if (current != null) {
if (current == sc) {
holder.title.setTextColor(convertView.getResources().getColor(
- R.color.bright_blue));
+ R.color.holo_blue_light));
holder.start.setTextColor(convertView.getResources().getColor(
- R.color.bright_blue));
+ R.color.holo_blue_light));
} else {
holder.title.setTextColor(defaultTextColor);
holder.start.setTextColor(defaultTextColor);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 445e4832c..efdf1a3c9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.adapter;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.support.v7.app.AlertDialog;
import android.widget.Toast;
import org.apache.commons.lang3.Validate;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 4ccff39af..9d7a509cf 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -7,11 +7,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
import java.util.Date;
@@ -50,7 +50,7 @@ public class DownloadLogAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.icon = (TextView) convertView.findViewById(R.id.txtvIcon);
- holder.retry = (Button) convertView.findViewById(R.id.btnRetry);
+ holder.retry = (IconButton) convertView.findViewById(R.id.btnRetry);
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
@@ -96,8 +96,6 @@ public class DownloadLogAdapter extends BaseAdapter {
if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE &&
!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.retry.setVisibility(View.VISIBLE);
- holder.retry.setText("{fa-repeat}");
- Iconify.addIcons(holder.retry);
holder.retry.setOnClickListener(clickListener);
ButtonHolder btnHolder;
if(holder.retry.getTag() != null) {
@@ -161,7 +159,7 @@ public class DownloadLogAdapter extends BaseAdapter {
static class Holder {
TextView icon;
- Button retry;
+ IconButton retry;
TextView title;
TextView type;
TextView date;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index 507d1adbc..1972e675e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -11,6 +11,7 @@ import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -39,6 +40,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
public static final int SELECTION_NONE = -1;
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
+
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
ActionButtonCallback callback,
@@ -52,6 +56,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.selectedItemIndex = SELECTION_NONE;
this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
+
+ playingBackGroundColor = context.getResources().getColor(R.color.highlight_light);
+ normalBackGroundColor = context.getResources().getColor(android.R.color.transparent);
}
@Override
@@ -80,6 +87,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
+ holder.container = (LinearLayout) convertView
+ .findViewById(R.id.container);
holder.title = (TextView) convertView
.findViewById(R.id.txtvItemname);
holder.lenSize = (TextView) convertView
@@ -174,9 +183,18 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.type.setImageBitmap(null);
holder.type.setVisibility(View.GONE);
}
+
+ if(media.isCurrentlyPlaying()) {
+ if(media.isCurrentlyPlaying()) {
+ holder.container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.container.setBackgroundColor(normalBackGroundColor);
+ }
+ }
}
- actionButtonUtils.configureActionButton(holder.butAction, item);
+ boolean isInQueue = itemAccess.isInQueue(item);
+ actionButtonUtils.configureActionButton(holder.butAction, item, isInQueue);
holder.butAction.setFocusable(false);
holder.butAction.setTag(item);
holder.butAction.setOnClickListener(butActionListener);
@@ -196,6 +214,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
};
static class Holder {
+ LinearLayout container;
TextView title;
TextView published;
TextView lenSize;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index 062f883bc..3e4dd4deb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -2,20 +2,20 @@ package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.support.annotation.Nullable;
import android.support.v4.view.MotionEventCompat;
-import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -36,7 +36,6 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.LongList;
@@ -59,7 +58,10 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private boolean locked;
- private int position = -1;
+ private FeedItem selectedItem;
+
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
public QueueRecyclerAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
@@ -72,6 +74,13 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
this.actionButtonCallback = actionButtonCallback;
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
+
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ }
+ normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
}
public void setLocked(boolean locked) {
@@ -88,17 +97,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
holder.itemView.setOnLongClickListener(v -> {
- position = pos;
+ selectedItem = item;
return false;
});
}
- public int getItemCount() {
- return itemAccess.getCount();
+ @Nullable
+ public FeedItem getSelectedItem() {
+ return selectedItem;
}
- public int getPosition() {
- return position;
+ public int getItemCount() {
+ return itemAccess.getCount();
}
public class ViewHolder extends RecyclerView.ViewHolder
@@ -106,6 +116,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
View.OnCreateContextMenuListener,
ItemTouchHelperViewHolder {
+ private final FrameLayout container;
private final ImageView dragHandle;
private final TextView placeholder;
private final ImageView cover;
@@ -120,6 +131,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
public ViewHolder(View v) {
super(v);
+ container = (FrameLayout) v.findViewById(R.id.container);
dragHandle = (ImageView) v.findViewById(R.id.drag_handle);
placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder);
cover = (ImageView) v.findViewById(R.id.imgvCover);
@@ -247,9 +259,15 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
progressBar.setVisibility(View.GONE);
}
+
+ if(media.isCurrentlyPlaying()) {
+ container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ container.setBackgroundColor(normalBackGroundColor);
+ }
}
- actionButtonUtils.configureActionButton(butSecondary, item);
+ actionButtonUtils.configureActionButton(butSecondary, item, true);
butSecondary.setFocusable(false);
butSecondary.setTag(item);
butSecondary.setOnClickListener(secondaryActionListener);
@@ -262,7 +280,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
.into(new CoverTarget(item.getFeed().getImageUri(), placeholder, cover));
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
index 3656c3638..3940eb8b6 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
@@ -1,13 +1,13 @@
package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import java.io.File;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
index 5486bc4fb..86636485d 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.R;
@@ -37,8 +37,7 @@ public class OpmlImportWorker extends
@Override
protected ArrayList<OpmlElement> doInBackground(Void... params) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Starting background work");
+ Log.d(TAG, "Starting background work");
if (mReader==null) {
return null;
@@ -72,21 +71,14 @@ public class OpmlImportWorker extends
}
progDialog.dismiss();
if (exception != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "An error occurred while trying to parse the opml document");
+ Log.d(TAG, "An error occurred while trying to parse the opml document");
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(R.string.error_label);
alert.setMessage(context.getString(R.string.opml_reader_error)
+ exception.getMessage());
- alert.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
-
- });
+ alert.setNeutralButton(android.R.string.ok, (dialog, which) -> {
+ dialog.dismiss();
+ });
alert.create().show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
index 1585f9b86..75b1bc8d2 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.CheckBox;
import android.widget.SeekBar;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
index 16fb77f2a..5f531e88f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.dialog;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.view.View;
@@ -26,28 +26,19 @@ public class GpodnetSetHostnameDialog {
et.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
dialog.setTitle(R.string.pref_gpodnet_sethostname_title)
.setView(setupContentView(context, et))
- .setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Editable e = et.getText();
- if (e != null) {
- GpodnetPreferences.setHostname(e.toString());
- }
- dialog.dismiss();
+ .setPositiveButton(R.string.confirm_label, (dialog1, which) -> {
+ final Editable e = et.getText();
+ if (e != null) {
+ GpodnetPreferences.setHostname(e.toString());
}
+ dialog1.dismiss();
})
- .setNegativeButton(R.string.cancel_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
+ .setNegativeButton(R.string.cancel_label, (dialog1, which) -> {
+ dialog1.cancel();
})
- .setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
- dialog.dismiss();
- }
+ .setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, (dialog1, which) -> {
+ GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
+ dialog1.dismiss();
})
.setCancelable(true);
return dialog.show();
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
index a699706a8..930079e40 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -17,11 +17,14 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
+import com.afollestad.materialdialogs.DialogAction;
+import com.afollestad.materialdialogs.MaterialDialog;
+
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
-public abstract class SleepTimerDialog extends Dialog {
+public abstract class SleepTimerDialog {
private static final String TAG = SleepTimerDialog.class.getSimpleName();
@@ -35,39 +38,54 @@ public abstract class SleepTimerDialog extends Dialog {
private String PREF_SHAKE_TO_RESET = "ShakeToReset";
private SharedPreferences prefs;
+ private MaterialDialog dialog;
private EditText etxtTime;
private Spinner spTimeUnit;
private CheckBox cbShakeToReset;
private CheckBox cbVibrate;
- private Button butConfirm;
- private Button butCancel;
+
private TimeUnit[] units = { TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS };
- public SleepTimerDialog(Context context, int titleTextId, int leftButtonTextId) {
- super(context);
+ public SleepTimerDialog(Context context) {
this.context = context;
prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- String[] spinnerContent = new String[] {
- context.getString(R.string.time_seconds),
- context.getString(R.string.time_minutes),
- context.getString(R.string.time_hours) };
-
- setContentView(R.layout.time_dialog);
- etxtTime = (EditText) findViewById(R.id.etxtTime);
- spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
- cbShakeToReset = (CheckBox) findViewById(R.id.cbShakeToReset);
- cbVibrate = (CheckBox) findViewById(R.id.cbVibrate);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
+ public MaterialDialog createNewDialog() {
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.set_sleeptimer_label);
+ builder.customView(R.layout.time_dialog, false);
+ builder.positiveText(R.string.set_sleeptimer_label);
+ builder.negativeText(R.string.cancel_label);
+ builder.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
+ }
- setTitle(R.string.set_sleeptimer_label);
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ try {
+ savePreferences();
+ long input = readTimeMillis();
+ onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
+ dialog.dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ }
+ });
+ dialog = builder.build();
+
+ View view = dialog.getView();
+ etxtTime = (EditText) view.findViewById(R.id.etxtTime);
+ spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit);
+ cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset);
+ cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate);
etxtTime.setText(prefs.getString(PREF_VALUE, "15"));
etxtTime.addTextChangedListener(new TextWatcher() {
@@ -84,15 +102,16 @@ public abstract class SleepTimerDialog extends Dialog {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
- etxtTime.postDelayed(new Runnable() {
- @Override
- public void run() {
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
- }
+ etxtTime.postDelayed(() -> {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
}, 100);
- ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(this.getContext(),
+ String[] spinnerContent = new String[] {
+ context.getString(R.string.time_seconds),
+ context.getString(R.string.time_minutes),
+ context.getString(R.string.time_hours) };
+ ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, spinnerContent);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spTimeUnit.setAdapter(spinnerAdapter);
@@ -102,40 +121,16 @@ public abstract class SleepTimerDialog extends Dialog {
cbShakeToReset.setChecked(prefs.getBoolean(PREF_SHAKE_TO_RESET, true));
cbVibrate.setChecked(prefs.getBoolean(PREF_VIBRATE, true));
- butConfirm.setText(R.string.set_sleeptimer_label);
- butConfirm.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- savePreferences();
- long input = readTimeMillis();
- onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
- dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- }
- });
-
- butCancel.setText(R.string.cancel_label);
- butCancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
+ return dialog;
}
private void checkInputLength(int length) {
if (length > 0) {
Log.d(TAG, "Length is larger than 0, enabling confirm button");
- butConfirm.setEnabled(true);
+ dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
} else {
Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- butConfirm.setEnabled(false);
+ dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index 5f6e3d0e2..4b512a48d 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.dialog;
-import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index cdd6bc265..23b5be25a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -5,7 +5,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
@@ -30,8 +29,10 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -43,8 +44,10 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
@@ -57,9 +60,7 @@ public class AllEpisodesFragment extends Fragment {
public static final String TAG = "AllEpisodesFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.FEED_LIST_UPDATE |
- EventDistributor.DOWNLOAD_QUEUED |
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
EventDistributor.UNREAD_ITEMS_UPDATE |
EventDistributor.PLAYER_STATUS_UPDATE;
@@ -80,8 +81,6 @@ public class AllEpisodesFragment extends Fragment {
private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
- private DownloadObserver downloadObserver = null;
-
private boolean isUpdatingFeeds;
protected Subscription subscription;
@@ -101,10 +100,6 @@ public class AllEpisodesFragment extends Fragment {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
this.activity.set((MainActivity) getActivity());
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
@@ -113,6 +108,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
+ EventBus.getDefault().registerSticky(this);
loadItems();
registerForContextMenu(recyclerView);
}
@@ -120,6 +116,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onPause() {
super.onPause();
+ EventBus.getDefault().unregister(this);
saveScrollPosition();
unregisterForContextMenu(recyclerView);
}
@@ -180,9 +177,6 @@ public class AllEpisodesFragment extends Fragment {
listAdapter = null;
activity.set(null);
viewsCreated = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
@@ -327,11 +321,10 @@ public class AllEpisodesFragment extends Fragment {
private void onFragmentLoaded() {
if (listAdapter == null) {
- listAdapter = new AllEpisodesRecycleAdapter(activity.get(), activity.get(), itemAccess,
- new DefaultActionButtonCallback(activity.get()), showOnlyNewEpisodes());
+ MainActivity mainActivity = activity.get();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
+ new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
recyclerView.setAdapter(listAdapter);
- downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
listAdapter.notifyDataSetChanged();
restoreScrollPosition();
@@ -339,21 +332,11 @@ public class AllEpisodesFragment extends Fragment {
updateShowOnlyEpisodesListViewState();
}
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged(List<Downloader> downloaderList) {
- AllEpisodesFragment.this.downloaderList = downloaderList;
- if (listAdapter != null) {
- listAdapter.notifyDataSetChanged();
- }
- }
- };
-
protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
- if (itemsLoaded) {
+ if (episodes != null) {
return episodes.size();
}
return 0;
@@ -361,7 +344,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public FeedItem getItem(int position) {
- if (itemsLoaded) {
+ if (episodes != null && position < episodes.size()) {
return episodes.get(position);
}
return null;
@@ -389,6 +372,39 @@ public class AllEpisodesFragment extends Fragment {
}
};
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(episodes == null || listAdapter == null) {
+ return;
+ }
+ for(int i=0, size = event.items.size(); i < size; i++) {
+ FeedItem item = event.items.get(i);
+ int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
+ if(pos >= 0) {
+ episodes.remove(pos);
+ episodes.add(pos, item);
+ listAdapter.notifyItemChanged(pos);
+ }
+ }
+ }
+
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(update.feedIds.length > 0) {
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ }
+ if(update.mediaIds.length > 0) {
+ if(listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
@@ -412,7 +428,7 @@ public class AllEpisodesFragment extends Fragment {
recyclerView.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
- subscription = Observable.defer(() -> Observable.just(loadData()))
+ subscription = Observable.fromCallable(() -> loadData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index c5b582d3a..a5568b16e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -156,7 +156,7 @@ public class CompletedDownloadsFragment extends ListFragment {
if (!itemsLoaded && viewCreated) {
setListShown(false);
}
- subscription = Observable.defer(() -> Observable.just(DBReader.getDownloadedItems()))
+ subscription = Observable.fromCallable(() -> DBReader.getDownloadedItems())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 669c6ac49..90cacd2e4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -147,7 +147,7 @@ public class DownloadLogFragment extends ListFragment {
if(subscription != null) {
subscription.unsubscribe();
}
- subscription = Observable.defer(() -> Observable.just(DBReader.getDownloadLog()))
+ subscription = Observable.fromCallable(() -> DBReader.getDownloadLog())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 532516dda..65305df3d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -17,7 +17,6 @@ import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-import de.greenrobot.event.EventBus;
/**
@@ -38,23 +37,11 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
protected String getPrefName() { return PREF_NAME; }
public void onEvent(FavoritesEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
loadItems();
}
@Override
- public void onStart() {
- super.onStart();
- EventBus.getDefault().register(this);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
protected void resetViewState() {
super.resetViewState();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index dc9f9740d..10d56d5cf 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -8,7 +8,6 @@ import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.util.Pair;
@@ -35,12 +34,16 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
+import org.apache.commons.lang3.ArrayUtils;
+
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -72,9 +75,7 @@ public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOAD_QUEUED |
- EventDistributor.UNREAD_ITEMS_UPDATE;
+ private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String ARG_FEEDITEM = "feeditem";
@@ -97,7 +98,6 @@ public class ItemFragment extends Fragment {
private FeedItem item;
private LongList queue;
private String webviewData;
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private ViewGroup root;
@@ -263,11 +263,7 @@ public class ItemFragment extends Fragment {
public void onResume() {
super.onResume();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
+ EventBus.getDefault().registerSticky(this);
if(itemsLoaded) {
updateAppearance();
}
@@ -294,9 +290,6 @@ public class ItemFragment extends Fragment {
}
private void resetViewState() {
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
toolbar.removeView(header);
}
@@ -319,8 +312,6 @@ public class ItemFragment extends Fragment {
"utf-8", "about:blank");
}
updateAppearance();
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
private void updateAppearance() {
@@ -486,27 +477,41 @@ public class ItemFragment extends Fragment {
public void onEventMainThread(QueueEvent event) {
if(event.contains(itemID)) {
- updateAppearance();
+ load();
}
}
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ for(FeedItem item : event.items) {
+ if(itemID == item.getId()) {
+ load();
+ return;
+ }
+ }
+ }
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(item == null || item.getMedia() == null) {
+ return;
+ }
+ long mediaId = item.getMedia().getId();
+ if(ArrayUtils.contains(update.mediaIds, mediaId)) {
+ if (itemsLoaded && getActivity() != null) {
updateAppearance();
}
}
- };
+ }
- private final DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
- public void onContentChanged(List<Downloader> downloaderList) {
- ItemFragment.this.downloaderList = downloaderList;
- if (itemsLoaded && getActivity() != null) {
- updateAppearance();
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ load();
}
}
};
@@ -515,7 +520,7 @@ public class ItemFragment extends Fragment {
if(subscription != null) {
subscription.unsubscribe();
}
- subscription = Observable.defer(() -> Observable.just(loadInBackground()))
+ subscription = Observable.fromCallable(() -> loadInBackground())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index ba3dfc2af..7bdeb8a44 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -9,7 +9,6 @@ import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
import android.support.v4.util.Pair;
@@ -46,17 +45,19 @@ import de.danoeh.antennapod.activity.FeedInfoActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -66,6 +67,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
@@ -85,9 +87,7 @@ import rx.schedulers.Schedulers;
public class ItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
- | EventDistributor.DOWNLOAD_QUEUED
- | EventDistributor.UNREAD_ITEMS_UPDATE
+ private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
| EventDistributor.PLAYER_STATUS_UPDATE;
public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
@@ -104,7 +104,6 @@ public class ItemlistFragment extends ListFragment {
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private MoreContentListFooterUtil listFooter;
@@ -147,11 +146,7 @@ public class ItemlistFragment extends ListFragment {
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
+ EventBus.getDefault().registerSticky(this);
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
@@ -193,9 +188,6 @@ public class ItemlistFragment extends ListFragment {
adapter = null;
viewsCreated = false;
listFooter = null;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@@ -394,29 +386,54 @@ public class ItemlistFragment extends ListFragment {
}
public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
loadItems();
}
public void onEvent(FeedEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
if(event.feedId == feedID) {
loadItems();
}
}
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ boolean queueChanged = false;
+ if(feed == null || feed.getItems() == null || adapter == null) {
+ return;
+ }
+ for(FeedItem item : event.items) {
+ int pos = FeedItemUtil.indexOfItemWithId(feed.getItems(), item.getId());
+ if(pos >= 0) {
+ loadItems();
+ return;
+ }
+ }
+ }
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(update.feedIds.length > 0) {
+ updateProgressBarVisibility();
+ }
+ if(update.mediaIds.length > 0) {
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+ }
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((EVENTS & arg) != 0) {
Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
- if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) {
- updateProgressBarVisibility();
- } else {
- loadItems();
- updateProgressBarVisibility();
- }
+ loadItems();
+ updateProgressBarVisibility();
}
}
};
@@ -444,8 +461,6 @@ public class ItemlistFragment extends ListFragment {
setupFooterView();
adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true);
setListAdapter(adapter);
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
refreshHeaderView();
setListShown(true);
@@ -485,22 +500,10 @@ public class ItemlistFragment extends ListFragment {
txtvInformation.setVisibility(View.GONE);
}
} else {
-
txtvInformation.setVisibility(View.GONE);
}
}
-
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged(List<Downloader> downloaderList) {
- ItemlistFragment.this.downloaderList = downloaderList;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
- };
-
private void setupHeaderView() {
if (getListView() == null || feed == null) {
Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
@@ -624,7 +627,7 @@ public class ItemlistFragment extends ListFragment {
subscription.unsubscribe();
}
- subscription = Observable.defer(() -> Observable.just(loadData()))
+ subscription = Observable.fromCallable(() -> loadData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index d684c064c..b996e1cb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -14,14 +14,12 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
-import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.event.QueueEvent;
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.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-import de.greenrobot.event.EventBus;
/**
@@ -42,23 +40,11 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
protected String getPrefName() { return PREF_NAME; }
public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
loadItems();
}
@Override
- public void onStart() {
- super.onStart();
- EventBus.getDefault().register(this);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- }
-
- @Override
protected void resetViewState() {
super.resetViewState();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index d7ffa3e23..b47a197c3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.res.TypedArray;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.ListFragment;
import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
@@ -21,7 +20,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -52,12 +52,20 @@ public class PlaybackHistoryFragment extends ListFragment {
private AtomicReference<Activity> activity = new AtomicReference<Activity>();
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private Subscription subscription;
@Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ this.activity.set(activity);
+ if (viewsCreated && itemsLoaded) {
+ onFragmentLoaded();
+ }
+ }
+
+ @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
@@ -65,8 +73,26 @@ public class PlaybackHistoryFragment extends ListFragment {
}
@Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // add padding
+ final ListView lv = getListView();
+ lv.setClipToPadding(false);
+ final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
+ lv.setPadding(0, vertPadding, 0, vertPadding);
+
+ viewsCreated = true;
+ if (itemsLoaded) {
+ onFragmentLoaded();
+ }
+ }
+
+
+ @Override
public void onResume() {
super.onResume();
+ EventBus.getDefault().registerSticky(this);
loadItems();
}
@@ -74,14 +100,18 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventBus.getDefault().unregister(this);
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
if(subscription != null) {
subscription.unsubscribe();
}
@@ -97,41 +127,18 @@ public class PlaybackHistoryFragment extends ListFragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- this.activity.set(activity);
- if (downloadObserver != null) {
- downloadObserver.setActivity(activity);
- downloadObserver.onResume();
- }
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
- }
-
- @Override
public void onDestroyView() {
super.onDestroyView();
adapter = null;
viewsCreated = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
-
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
+ public void onEvent(DownloadEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
}
}
@@ -205,24 +212,12 @@ public class PlaybackHistoryFragment extends ListFragment {
// it harder to read.
adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true, false);
setListAdapter(adapter);
- downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
}
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged(List<Downloader> downloaderList) {
- PlaybackHistoryFragment.this.downloaderList = downloaderList;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
- };
-
private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public boolean isInQueue(FeedItem item) {
@@ -257,7 +252,7 @@ public class PlaybackHistoryFragment extends ListFragment {
if(subscription != null) {
subscription.unsubscribe();
}
- subscription = Observable.defer(() -> Observable.just(loadData()))
+ subscription = Observable.fromCallable(() -> loadData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index 3c3f7f01c..e13fd76b3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,7 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
@@ -25,15 +24,15 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
-import java.util.Collections;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -68,7 +67,6 @@ public class QueueFragment extends Fragment {
public static final String TAG = "QueueFragment";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOAD_QUEUED |
EventDistributor.PLAYER_STATUS_UPDATE;
private TextView infoBar;
@@ -86,13 +84,6 @@ public class QueueFragment extends Fragment {
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
- private DownloadObserver downloadObserver = null;
-
- /**
- * Download observer updates won't result in an upate of the list adapter if this is true.
- */
- private boolean blockDownloadObserverUpdate = false;
-
private Subscription subscription;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
@@ -108,10 +99,6 @@ public class QueueFragment extends Fragment {
@Override
public void onStart() {
super.onStart();
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
if (queue != null) {
onFragmentLoaded();
}
@@ -123,7 +110,7 @@ public class QueueFragment extends Fragment {
recyclerView.setAdapter(recyclerAdapter);
loadItems();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
+ EventBus.getDefault().registerSticky(this);
}
@Override
@@ -138,7 +125,10 @@ public class QueueFragment extends Fragment {
}
public void onEventMainThread(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(queue == null || recyclerAdapter == null) {
+ return;
+ }
switch(event.action) {
case ADDED:
queue.add(event.position, event.item);
@@ -163,17 +153,17 @@ public class QueueFragment extends Fragment {
recyclerAdapter.notifyDataSetChanged();
break;
case MOVED:
- int from = FeedItemUtil.indexOfItemWithId(queue, event.item.getId());
- int to = event.position;
- Collections.swap(queue, from, to);
- recyclerAdapter.notifyItemMoved(from, to);
- break;
+ return;
}
+ saveScrollPosition();
onFragmentLoaded();
}
public void onEventMainThread(FeedItemEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(queue == null || recyclerAdapter == null) {
+ return;
+ }
for(int i=0, size = event.items.size(); i < size; i++) {
FeedItem item = event.items.get(i);
int pos = FeedItemUtil.indexOfItemWithId(queue, item.getId());
@@ -185,6 +175,21 @@ public class QueueFragment extends Fragment {
}
}
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (update.feedIds.length > 0) {
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ } else if (update.mediaIds.length > 0) {
+ if (recyclerAdapter != null) {
+ recyclerAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
@@ -208,20 +213,11 @@ public class QueueFragment extends Fragment {
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
if (position > 0 || offset > 0) {
layoutManager.scrollToPositionWithOffset(position, (int) offset);
- // restore once, then forget
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, 0);
- editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
- editor.commit();
}
}
private void resetViewState() {
recyclerAdapter = null;
- blockDownloadObserverUpdate = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
@Override
@@ -327,20 +323,33 @@ public class QueueFragment extends Fragment {
if(!isVisible()) {
return false;
}
- int pos = recyclerAdapter.getPosition();
- FeedItem selectedItem = itemAccess.getItem(pos);
-
+ FeedItem selectedItem = recyclerAdapter.getSelectedItem();
if (selectedItem == null) {
- Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection");
+ Log.i(TAG, "Selected item was null, ignoring selection");
return super.onContextItemSelected(item);
}
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
+ switch(item.getItemId()) {
+ case R.id.move_to_top_item:
+ int position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
+ queue.add(0, queue.remove(position));
+ recyclerAdapter.notifyItemMoved(position, 0);
+ DBWriter.moveQueueItemToTop(selectedItem.getId(), true);
+ return true;
+ case R.id.move_to_bottom_item:
+ position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
+ queue.add(queue.size()-1, queue.remove(position));
+ recyclerAdapter.notifyItemMoved(position, queue.size()-1);
+ DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
+ return true;
+ default:
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
}
}
@@ -367,9 +376,9 @@ public class QueueFragment extends Fragment {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Log.d(TAG, "move(" + from + ", " + to + ")");
- Collections.swap(queue, from, to);
+ queue.add(to, queue.remove(from));
recyclerAdapter.notifyItemMoved(from, to);
- DBWriter.moveQueueItem(from, to, false);
+ DBWriter.moveQueueItem(from, to, true);
return true;
}
@@ -447,8 +456,6 @@ public class QueueFragment extends Fragment {
recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
new DefaultActionButtonCallback(activity), itemTouchHelper);
recyclerView.setAdapter(recyclerAdapter);
- downloadObserver = new DownloadObserver(activity, new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
if(queue == null || queue.size() == 0) {
recyclerView.setVisibility(View.GONE);
@@ -482,16 +489,6 @@ public class QueueFragment extends Fragment {
infoBar.setText(info);
}
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged(List<Downloader> downloaderList) {
- QueueFragment.this.downloaderList = downloaderList;
- if (recyclerAdapter != null && !blockDownloadObserverUpdate) {
- recyclerAdapter.notifyDataSetChanged();
- }
- }
- };
-
private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() {
@Override
public int getCount() {
@@ -500,7 +497,10 @@ public class QueueFragment extends Fragment {
@Override
public FeedItem getItem(int position) {
- return queue != null ? queue.get(position) : null;
+ if(queue != null && position < queue.size()) {
+ return queue.get(position);
+ }
+ return null;
}
@Override
@@ -553,6 +553,7 @@ public class QueueFragment extends Fragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
+ Log.d(TAG, "arg: " + arg);
loadItems();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
getActivity().supportInvalidateOptionsMenu();
@@ -562,6 +563,7 @@ public class QueueFragment extends Fragment {
};
private void loadItems() {
+ Log.d(TAG, "loadItems()");
if(subscription != null) {
subscription.unsubscribe();
}
@@ -570,7 +572,7 @@ public class QueueFragment extends Fragment {
txtvEmpty.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
- subscription = Observable.defer(() -> Observable.just(DBReader.getQueue()))
+ subscription = Observable.fromCallable(() -> DBReader.getQueue())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 544bdfc43..d81d18640 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.View;
@@ -12,7 +11,8 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -20,25 +20,18 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.greenrobot.event.EventBus;
/**
* Displays all running downloads and provides actions to cancel them
*/
public class RunningDownloadsFragment extends ListFragment {
+
private static final String TAG = "RunningDownloadsFrag";
- private DownloadObserver downloadObserver;
+ private DownloadlistAdapter adapter;
private List<Downloader> downloaderList;
-
- @Override
- public void onDetach() {
- super.onDetach();
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
- }
-
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -49,20 +42,39 @@ public class RunningDownloadsFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- final DownloadlistAdapter downloadlistAdapter = new DownloadlistAdapter(getActivity(), itemAccess);
- setListAdapter(downloadlistAdapter);
+ adapter = new DownloadlistAdapter(getActivity(), itemAccess);
+ setListAdapter(adapter);
+ }
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), new DownloadObserver.Callback() {
- @Override
- public void onContentChanged(List<Downloader> downloaderList) {
- Log.d(TAG, "onContentChanged: downloaderList.size() == " + downloaderList.size());
- RunningDownloadsFragment.this.downloaderList = downloaderList;
- downloadlistAdapter.notifyDataSetChanged();
- }
- });
- downloadObserver.onResume();
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventBus.getDefault().registerSticky(this);
}
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ setListAdapter(null);
+ adapter = null;
+ }
+
+ public void onEvent(DownloadEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+
private DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
@Override
public int getCount() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index edd8cdd1a..43354ad28 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -204,7 +204,7 @@ public class SearchFragment extends ListFragment {
if (viewCreated && !itemsLoaded) {
setListShown(false);
}
- subscription = Observable.defer(() -> Observable.just(performSearch()))
+ subscription = Observable.fromCallable(() -> performSearch())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 3fa1048c0..0197cc88c 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -186,11 +186,6 @@ public class FeedItemMenuHandler {
GpodnetPreferences.enqueueEpisodeAction(actionNew);
}
break;
- case R.id.move_to_top_item:
- DBWriter.moveQueueItemToTop(selectedItem.getId(), true);
- return true;
- case R.id.move_to_bottom_item:
- DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
case R.id.add_to_queue_item:
DBWriter.addQueueItem(context, selectedItem);
break;
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index f32a1c2ee..84da32a40 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -1,10 +1,10 @@
package de.danoeh.antennapod.menuhandler;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java
deleted file mode 100644
index 898a56004..000000000
--- a/app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.os.Build;
-import android.preference.EditTextPreference;
-import android.util.AttributeSet;
-
-import de.danoeh.antennapod.R;
-
-public class CustomEditTextPreference extends EditTextPreference {
-
- public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public CustomEditTextPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public CustomEditTextPreference(Context context) {
- super(context);
- }
-
- @Override
- protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
- if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- builder.setInverseBackgroundForced(true);
- getEditText().setTextColor(getContext().getResources().getColor(R.color.black));
- }
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index 138246038..73d7da0f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.preferences;
+import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -17,7 +17,10 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
import android.text.Editable;
+import android.text.Html;
import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.util.Log;
@@ -31,7 +34,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
@@ -41,6 +43,8 @@ import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
@@ -50,9 +54,11 @@ import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/**
* Sets up a preference UI that lets the user change user preferences.
*/
+
public class PreferenceController implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "PreferenceController";
+
public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
@@ -158,21 +164,22 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
);
-
- ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- activity.startActivityForResult(
- new Intent(activity,
- DirectoryChooserActivity.class),
- DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED
- );
- return true;
- }
- }
- );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
+ .setOnPreferenceClickListener(
+ new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if(Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ }
+ );
ui.findPreference(UserPreferences.PREF_THEME)
.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
@@ -394,16 +401,37 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
updateGpodnetPreferenceScreen();
}
+ @SuppressLint("NewApi")
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
- String dir = data
- .getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Setting data folder");
- UserPreferences.setDataFolder(dir);
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path = new File(dir);
+ String message = null;
+ final Context context= ui.getActivity().getApplicationContext();
+ if(!path.exists()) {
+ message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(context.getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(context.getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ setDataFolderText();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(ui.getActivity());
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
}
}
+
private void updateGpodnetPreferenceScreen() {
final boolean loggedIn = GpodnetPreferences.loggedIn();
ui.findPreference(PreferenceController.PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
@@ -564,9 +592,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
);
boolean newValue = ((CheckBoxPreference) preference)
.isChecked();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Selected network " + key
- + ". New state: " + newValue);
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
int index = prefValuesList.indexOf(key);
if (index >= 0 && newValue == false) {
@@ -649,6 +675,47 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
builder.create().show();
}
+ private void showChooseDataFolderDialog() {
+ Context context = ui.getActivity();
+ String dataFolder = UserPreferences.getDataFolder(context, null).getAbsolutePath();
+ int selectedIndex = -1;
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null);
+ String[] folders = new String[mediaDirs.length];
+ CharSequence[] choices = new CharSequence[mediaDirs.length];
+ for(int i=0; i < mediaDirs.length; i++) {
+ String path = folders[i] = mediaDirs[i].getAbsolutePath();
+ if(dataFolder.equals(path)) {
+ selectedIndex = i;
+ }
+ int index = path.indexOf("Android");
+ if(index >= 0) {
+ choices[i] = path.substring(0, index);
+ } else {
+ choices[i] = path;
+ }
+ long bytes = StorageUtils.getFreeSpaceAvailable();
+ String freeSpace = String.format(context.getString(R.string.free_space_label),
+ Converter.byteToString(bytes));
+ choices[i] = Html.fromHtml("<html><small>" + choices[i]
+ + " [" + freeSpace + "]" + "</small></html>");
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.choose_data_directory)
+ .content(R.string.choose_data_directory_message)
+ .items(choices)
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders[which];
+ Log.d(TAG, "data folder: " + folder);
+ UserPreferences.setDataFolder(folder);
+ setDataFolderText();
+ return true;
+ })
+ .negativeText(R.string.cancel_label)
+ .cancelable(true)
+ .build();
+ dialog.show();
+ }
+
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = ui.getActivity();
diff --git a/app/src/main/res/layout-v14/directory_chooser.xml b/app/src/main/res/layout-v14/directory_chooser.xml
index 8f9c4ee93..14e2f6a38 100644
--- a/app/src/main/res/layout-v14/directory_chooser.xml
+++ b/app/src/main/res/layout-v14/directory_chooser.xml
@@ -101,7 +101,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/butNavUp"
- android:background="@color/bright_blue" />
+ android:background="@color/holo_blue_light" />
</RelativeLayout>
<ListView
diff --git a/app/src/main/res/layout-v14/download_authentication_activity.xml b/app/src/main/res/layout-v14/download_authentication_activity.xml
index 8d2c7fb17..f6925dc3a 100644
--- a/app/src/main/res/layout-v14/download_authentication_activity.xml
+++ b/app/src/main/res/layout-v14/download_authentication_activity.xml
@@ -12,7 +12,7 @@
android:layout_alignParentTop="true"
android:textSize="@dimen/text_size_large"
android:layout_margin="16dp"
- android:textColor="@color/bright_blue"
+ android:textColor="@color/holo_blue_light"
android:textStyle="italic"/>
<TextView
diff --git a/app/src/main/res/layout-v14/time_dialog.xml b/app/src/main/res/layout-v14/time_dialog.xml
index aefb82b8b..06c2cce14 100644
--- a/app/src/main/res/layout-v14/time_dialog.xml
+++ b/app/src/main/res/layout-v14/time_dialog.xml
@@ -1,21 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/etxtTime"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="8dp"
- android:ems="7"
+ android:ems="2"
android:hint="@string/enter_time_here_label"
android:inputType="number"
android:maxLength="2" >
@@ -25,18 +26,13 @@
<Spinner
android:id="@+id/spTimeUnit"
- android:layout_width="180dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" />
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center">
-
- <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -58,52 +54,6 @@
android:layout_height="wrap_content"
android:text="@string/timer_vibration_label"/>
- </LinearLayout>
-
</LinearLayout>
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="?android:attr/dividerVertical" />
-
- <View
- android:id="@+id/horizontal_divider"
- android:layout_width="1dip"
- android:layout_height="fill_parent"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:background="?android:attr/dividerVertical" />
-
- <Button
- android:id="@+id/butCancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/cancel_label" />
-
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/directory_chooser.xml b/app/src/main/res/layout/directory_chooser.xml
index 93cd1c0d3..635a73cf4 100644
--- a/app/src/main/res/layout/directory_chooser.xml
+++ b/app/src/main/res/layout/directory_chooser.xml
@@ -78,7 +78,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/butNavUp"
- android:background="@color/bright_blue" />
+ android:background="@color/holo_blue_light" />
</RelativeLayout>
<ListView
diff --git a/app/src/main/res/layout/download_authentication_activity.xml b/app/src/main/res/layout/download_authentication_activity.xml
index b035f2516..27604973a 100644
--- a/app/src/main/res/layout/download_authentication_activity.xml
+++ b/app/src/main/res/layout/download_authentication_activity.xml
@@ -12,7 +12,7 @@
android:layout_alignParentTop="true"
android:textSize="@dimen/text_size_large"
android:layout_margin="16dp"
- android:textColor="@color/bright_blue"
+ android:textColor="@color/holo_blue_light"
android:textStyle="italic"/>
<TextView
diff --git a/app/src/main/res/layout/downloadlog_item.xml b/app/src/main/res/layout/downloadlog_item.xml
index c6a34a517..20d879933 100644
--- a/app/src/main/res/layout/downloadlog_item.xml
+++ b/app/src/main/res/layout/downloadlog_item.xml
@@ -20,7 +20,7 @@
tools:text="[Icon]"
android:gravity="center" />
- <Button
+ <com.joanzapata.iconify.widget.IconButton
android:id="@+id/btnRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -28,6 +28,7 @@
android:layout_alignLeft="@id/txtvIcon"
android:layout_alignRight="@id/txtvIcon"
android:layout_marginTop="8dp"
+ android:text="{fa-repeat}"
tools:text="↻" />
<TextView
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index f9608fafc..ef83baa17 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -13,7 +13,7 @@
<View
android:layout_width="match_parent"
android:layout_height="2dp"
- android:background="@color/bright_blue"/>
+ android:background="@color/holo_blue_light"/>
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index edae51847..2b49b4b35 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -46,7 +46,7 @@
android:layout_height="1dp"
android:layout_below="@id/imgvCover"
android:layout_marginTop="8dp"
- android:background="@color/bright_blue"/>
+ android:background="@color/holo_blue_light"/>
</RelativeLayout>
<ScrollView
diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml
index 6b7c45978..d2b85e7df 100644
--- a/app/src/main/res/layout/feeditemlist_item.xml
+++ b/app/src/main/res/layout/feeditemlist_item.xml
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<RelativeLayout
-
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
@@ -103,12 +104,10 @@
android:indeterminate="false"
/>
-
-
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>
<include layout="@layout/secondary_action"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index 7a5652c45..cde1b4aa6 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -1,11 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+ android:id="@+id/container"
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+<LinearLayout
+ android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
- android:orientation="horizontal"
android:background="?attr/selectableItemBackground"
+ android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<RelativeLayout
@@ -127,4 +133,6 @@
<include layout="@layout/secondary_action" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
+
+</FrameLayout>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 8572f5e05..d950f11d2 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
+ android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+<LinearLayout
+ android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
+ android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="8dp"
android:gravity="center_vertical"
- android:background="?attr/selectableItemBackground"
tools:background="@android:color/darker_gray" >
<ImageView
@@ -135,4 +140,6 @@
<include layout="@layout/secondary_action"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
+
+</FrameLayout>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index e387a1689..b270e82f7 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,46 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:orientation="horizontal">
<EditText
android:id="@+id/etxtTime"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="8dp"
- android:ems="7"
+ android:ems="2"
android:hint="@string/enter_time_here_label"
- android:inputType="number"
- android:maxLength="2" >
-
- </EditText>
+ android:inputType="number"
+ android:maxLength="2" />
<Spinner
android:id="@+id/spTimeUnit"
- android:layout_width="180dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" />
+
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
@@ -59,30 +53,6 @@
android:layout_height="wrap_content"
android:text="@string/timer_vibration_label"/>
- </LinearLayout>
-
- </LinearLayout>
-
- <LinearLayout
- style="@android:style/ButtonBar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginRight="8dp"
- android:layout_weight="1"
- tools:text="Confirm" />
-
- <Button
- android:id="@+id/butCancel"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- tools:text="Cancel" />
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 09cf48b02..38350155a 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -2,7 +2,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/user_interface_label">
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:entryValues="@array/theme_values"
android:entries="@array/theme_options"
android:title="@string/pref_set_theme_title"
@@ -17,14 +17,14 @@
android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum"
android:title="@string/pref_nav_drawer_items_title" />
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:entryValues="@array/nav_drawer_feed_order_values"
android:entries="@array/nav_drawer_feed_order_options"
android:title="@string/pref_nav_drawer_feed_order_title"
android:key="prefDrawerFeedOrder"
android:summary="@string/pref_nav_drawer_feed_order_sum"
android:defaultValue="0"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:entryValues="@array/nav_drawer_feed_counter_values"
android:entries="@array/nav_drawer_feed_counter_options"
android:title="@string/pref_nav_drawer_feed_counter_title"
@@ -107,7 +107,7 @@
android:key="prefAutoDelete"
android:summary="@string/pref_auto_delete_sum"
android:title="@string/pref_auto_delete_title"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="30"
android:entries="@array/smart_mark_as_played_values"
android:entryValues="@array/smart_mark_as_played_values"
@@ -145,7 +145,7 @@
android:summary="@string/pref_mobileUpdate_sum"
android:title="@string/pref_mobileUpdate_title"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="-1"
android:entries="@array/episode_cleanup_entries"
android:key="prefEpisodeCleanup"
@@ -153,12 +153,12 @@
android:summary="@string/pref_episode_cleanup_summary"
android:entryValues="@array/episode_cleanup_values"/>
- <de.danoeh.antennapod.preferences.CustomEditTextPreference
- android:defaultValue="6"
+ <com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
+ android:defaultValue="4"
android:inputType="number"
android:key="prefParallelDownloads"
android:title="@string/pref_parallel_downloads_title"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="20"
android:entries="@array/episode_cache_size_entries"
android:key="prefEpisodeCacheSize"
diff --git a/build.gradle b/build.gradle
index ab6884c76..56527f0fc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -49,6 +49,7 @@ project.ext {
glideVersion = "3.6.1"
jsoupVersion = "1.7.3"
rxAndroidVersion = "1.0.1"
+ rxJavaVersion = "1.0.16"
okhttpVersion = "2.5.0"
okioVersion = "1.6.0"
}
diff --git a/changelog/ko_KR.md b/changelog/ko_KR.md
new file mode 100644
index 000000000..6776f0a7a
--- /dev/null
+++ b/changelog/ko_KR.md
@@ -0,0 +1,327 @@
+Change Log
+==========
+
+Version 1.4
+-----------
+* BLUETOOTH PERMISSION: Needed to be able to resume playback when a Bluetooth device reconnects with your phone
+* VIBRATE PERMISSION: Used optionally with the sleep timer
+* Native variable speed playback (experimental via options)
+* Improved sleep timer
+* Mark episodes as 'favorite'
+* Notification can skip episodes
+* Keep episodes when skipping them
+* Episode art on lock screen
+* Flexible episode cleanup
+* Rewind after pause
+* Usability improvements
+* Bug fixes
+
+Version 1.3
+-----------
+* Bulk actions on feed episodes (download, queue, delete)
+* Reduced space used by images
+* Automatic refresh at a certain time of day
+* Customizable indicators and sorting for feeds
+* Ability to share feeds
+* Improved auto download
+* Many fixes and usability improvements
+
+Version 1.2
+-----------
+* Optionally disable swiping and dragging in the queue
+* Resume playback after phone call
+* Filter episodes in the Podcast feed
+* Hide items in the Nav drawer
+* Customize times for fast forward and rewind
+* Resolved issues with opening some OPML files
+* Various bug fixes and usability improvements
+
+Version 1.1
+-----------
+* iTunes podcast integration
+* Swipe to remove items from the queue
+* Set the number of parallel downloads
+* Fix for gpodder.net on old devices
+* Fixed date problems for some feeds
+* Display improvements
+* Usability improvements
+* Several other bugfixes
+
+Version 1.0
+-----------
+* The queue can now be sorted
+* Added option to delete episode after playback
+* Fixed a bug that caused chapters to be displayed multiple times
+* Several other improvements and bugfixes
+
+
+Version 0.9.9.6
+---------------
+* Fixed problems related to variable playback speed plugins
+* Fixed automatic feed update problems
+* Several other bugfixes and improvements
+
+Version 0.9.9.5
+---------------
+* Added support for paged feeds
+* Improved user interface
+* Added Japanese and Turkish translations
+* Fixed more image loading problems
+* Other bugfixes and improvements
+
+Version 0.9.9.4
+---------------
+* Added option to keep notification and lockscreen controls when playback is paused
+* Fixed a bug where episode images were not loaded correctly
+* Fixed battery usage problems
+
+Version 0.9.9.3
+---------------
+* Fixed video playback problems
+* Improved image loading
+* Other bugfixes and improvements
+
+Version 0.9.9.2
+---------------
+* Added support for feed discovery if a website URL is entered
+* Added support for 'next'/'previous' media keys
+* Improved sleep timer
+* Timestamps in shownotes can now be used to jump to a specific position
+* Automatic Flattring is now configurable
+* Several bugfixes and improvements
+
+Version 0.9.9.1
+---------------
+* Several bugfixes and improvements
+
+Version 0.9.9.0
+---------------
+* New user interface
+* Failed downloads are now resumed when restarted
+* Added support for Podlove Alternate Feeds
+* Added support for "pcast"-protocol
+* Added backup & restore functionality. This feature has to be enabled in the Android settings in order to work
+* Several bugfixes and improvements
+
+Version 0.9.8.3
+---------------
+* Added support for password-protected feeds and episodes
+* Added support for more types of episode images
+* Added Hebrew translation
+* Several bugfixes and improvements
+
+Version 0.9.8.2
+---------------
+* Several bugfixes and improvements
+* Added Korean translation
+
+Version 0.9.8.1
+---------------
+* Added option to flattr an episode automatically after 80 percent of the episode have been played
+* Added Polish translation
+* Several bugfixes and improvements
+
+Version 0.9.8.0
+---------------
+* Added access to the gpodder.net directory
+* Added ability to sync podcast subscriptions with the gpodder.net service
+* Automatic download can now be turned on or off for specific podcasts
+* Added option to pause playback when another app is playing sounds
+* Added Dutch and Hindi translation
+* Resolved a problem with automatic podcast updates
+* Resolved a problem with the buttons' visibility in the episode screen
+* Resolved a problem where episodes would be re-downloaded unnecessarily
+* Several other bugfixes and usability improvements
+
+Version 0.9.7.5
+---------------
+* Reduced application startup time
+* Reduced memory usage
+* Added option to change the playback speed
+* Added Swedish translation
+* Several bugfixes and improvements
+
+Version 0.9.7.4
+---------------
+* Episode cache size can now be set to unlimited
+* Removing an episode in the queue via sliding can now be undone
+* Added support for Links in MP3 chapters
+* Added Czech(Czech Republic), Azerbaijani and Portuguese translations
+* Several bugfixes and improvements
+
+Version 0.9.7.3
+---------------
+* Bluetooth devices now display metadata during playback (requires AVRCP 1.3 or higher)
+* User interface improvements
+* Several bugfixes
+
+Version 0.9.7.2
+---------------
+* Automatic download can now be disabled
+* Added Italian (Italy) translation
+* Several bugfixes
+
+Version 0.9.7.1
+---------------
+* Added automatic download of new episodes
+* Added option to specify the number of downloaded episodes to keep on the device
+* Added support for playback of external media files
+* Several improvements and bugfixes
+* Added Catalan translation
+
+Version 0.9.7
+-------------
+* Improved user interface
+* OPML files can now be imported by selecting them in a file browser
+* The queue can now be organized via drag & drop
+* Added expandable notifications (only supported on Android 4.1 and above)
+* Added Danish, French, Romanian (Romania) and Ukrainian (Ukraine) translation (thanks to all translators!)
+* Several bugfixes and minor improvements
+
+Version 0.9.6.4
+---------------
+* Added Chinese translation (Thanks tupunco!)
+* Added Portuguese (Brazil) translation (Thanks mbaltar!)
+* Several bugfixes
+
+Version 0.9.6.3
+---------------
+* Added the ability change the location of AntennaPod's data folder
+* Added Spanish translation (Thanks frandavid100!)
+* Solved problems with several feeds
+
+Version 0.9.6.2
+---------------
+* Fixed import problems with some OPML files
+* Fixed download problems
+* AntennaPod now recognizes changes of episode information
+* Other improvements and bugfixes
+
+Version 0.9.6.1
+---------------
+* Added dark theme
+* Several bugfixes and improvements
+
+Version 0.9.6
+-------------
+* Added support for VorbisComment chapters
+* AntennaPod now shows items as 'in progress' when playback has started
+* Reduced memory usage
+* Added support for more feed types
+* Several bugfixes
+
+
+Version 0.9.5.3
+---------------
+* Fixed crash when trying to start playback on some devices
+* Fixed problems with some feeds
+* Other bugfixes and improvements
+
+Version 0.9.5.2
+---------------
+* Media player now doesn't use network bandwidth anymore if not in use
+* Other improvements and bugfixes
+
+Version 0.9.5.1
+---------------
+* Added playback history
+* Improved behavior of download report notifications
+* Improved support for headset controls
+* Bugfixes in the feed parser
+* Moved 'OPML import' button into the 'add feed' screen and the 'OPML export' button into the settings screen
+
+Version 0.9.5
+-------------
+* Experimental support for MP3 chapters
+* New menu options for the 'new' list and the queue
+* Auto-delete feature
+* Better Download error reports
+* Several Bugfixes
+
+Version 0.9.4.6
+---------------
+* Enabled support for small-screen devices
+* Disabling the sleep timer should now work again
+
+Version 0.9.4.5
+---------------
+* Added Russian translation (Thanks older!)
+* Added German translation
+* Several bugfixes
+
+Version 0.9.4.4
+---------------
+* Added player controls at the bottom of the main screen and the feedlist screens
+* Improved media playback
+
+Version 0.9.4.3
+---------------
+* Fixed several bugs in the feed parser
+* Improved behavior of download reports
+
+Version 0.9.4.2
+---------------
+* Fixed bug in the OPML importer
+* Reduced memory usage of images
+* Fixed download problems on some devices
+
+Version 0.9.4.1
+---------------
+* Changed behavior of download notifications
+
+Version 0.9.4
+-------------
+* Faster and more reliable downloads
+* Added lockscreen player controls for Android 4.x devices
+* Several bugfixes
+
+Version 0.9.3.1
+---------------
+* Added preference to hide feed items which don't have an episode
+* Improved image size for some some screen sizes
+* Added grid view for large screens
+* Several bugfixes
+
+Version 0.9.3
+-------------
+* MiroGuide integration
+* Bugfixes in the audio- and videoplayer
+* Automatically add feeds to the queue when they have been downloaded
+
+Version 0.9.2
+-------------
+* Bugfixes in the user interface
+* GUID and ID attributes are now recognized by the Feedparser
+* Stability improvements when adding several feeds at the same time
+* Fixed bugs that occured when adding certain feeds
+
+Version 0.9.1.1
+--------------------
+* Changed Flattr credentials
+* Improved layout of Feed information screen
+* AntennaPod is now open source! The source code is available at https://github.com/danieloeh/AntennaPod
+
+Version 0.9.1
+-----------------
+* Added support for links in SimpleChapters
+* Bugfix: Current Chapter wasn't always displayed correctly
+
+Version 0.9
+--------------
+
+* OPML export
+* Flattr integration
+* Sleep timer
+
+Version 0.8.2
+-------------
+
+* Added search
+* Improved OPML import experience
+* More bugfixes
+
+Version 0.8.1
+------------
+
+* Added support for SimpleChapters
+* OPML import \ No newline at end of file
diff --git a/changelog/ru_RU.md b/changelog/ru_RU.md
index 3f419cb50..60f512495 100644
--- a/changelog/ru_RU.md
+++ b/changelog/ru_RU.md
@@ -1,13 +1,13 @@
Список изменений
==========
-Version 1.4
+Версия 1.4
-----------
-* BLUETOOTH PERMISSION: Needed to be able to resume playback when a Bluetooth device reconnects with your phone
-* VIBRATE PERMISSION: Used optionally with the sleep timer
-* Native variable speed playback (experimental via options)
+* ДОСТУП К BLUETOOTH: Используется для продолжения воспроизведения, когда Bluetooth-устройство переподключается к вашему телефону
+* ДОСТУП К ВИБРАЦИИ: Используется дополнительно с таймером сна
+* Переменная скорость воспроизведения (экспериментально доступна в опциях)
* Улучшен таймер сна
-* Mark episodes as 'favorite'
+* Пометить эпизоды как 'избранные'
* Notification can skip episodes
* Keep episodes when skipping them
* Episode art on lock screen
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
deleted file mode 100644
index 2ce506c22..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java
+++ /dev/null
@@ -1,191 +0,0 @@
-package de.danoeh.antennapod.core.asynctask;
-
-import android.app.Activity;
-import android.content.*;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.Log;
-
-import org.apache.commons.lang3.Validate;
-
-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;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Provides access to the DownloadService's list of items that are currently being downloaded.
- * The DownloadObserver object should be created in the activity's onCreate() method. resume() and pause()
- * should be called in the activity's onResume() and onPause() methods
- */
-public class DownloadObserver {
- private static final String TAG = "DownloadObserver";
-
- /**
- * Time period between update notifications.
- */
- public static final int WAITING_INTERVAL_MS = 3000;
-
- private volatile Activity activity;
- private final Handler handler;
- private final Callback callback;
-
- private DownloadService downloadService = null;
- private AtomicBoolean mIsBound = new AtomicBoolean(false);
-
- private Thread refresherThread;
- private AtomicBoolean refresherThreadRunning = new AtomicBoolean(false);
-
- private AtomicInteger users = new AtomicInteger(0);
-
-
- /**
- * Creates a new download observer.
- *
- * @param activity Used for registering receivers
- * @param handler All callback methods are executed on this handler. The handler MUST run on the GUI thread.
- * @param callback Callback methods for posting content updates
- * @throws java.lang.IllegalArgumentException if one of the arguments is null.
- */
- public DownloadObserver(Activity activity, Handler handler, Callback callback) {
- Validate.notNull(activity);
- Validate.notNull(handler);
- Validate.notNull(callback);
-
- this.activity = activity;
- this.handler = handler;
- this.callback = callback;
- }
-
- public void onResume() {
- Log.d(TAG, "DownloadObserver resumed");
- if(users.getAndIncrement() == 0) {
- activity.registerReceiver(contentChangedReceiver, new IntentFilter(DownloadService.ACTION_DOWNLOADS_CONTENT_CHANGED));
- connectToDownloadService();
- }
- }
-
- public void onPause() {
- Log.d(TAG, "DownloadObserver paused");
- if(users.decrementAndGet() > 0) {
- return;
- }
- try {
- activity.unregisterReceiver(contentChangedReceiver);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- try {
- activity.unbindService(mConnection);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- stopRefresher();
- }
-
- private BroadcastReceiver contentChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // reconnect to DownloadService if connection has been closed
- if (downloadService == null) {
- connectToDownloadService();
- }
- 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(List<Downloader> downloaderList);
- }
-
- private void connectToDownloadService() {
- activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0);
- }
-
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceDisconnected(ComponentName className) {
- downloadService = null;
- mIsBound.set(false);
- stopRefresher();
- Log.i(TAG, "Closed connection with DownloadService.");
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- downloadService = ((DownloadService.LocalBinder) service)
- .getService();
- mIsBound.set(true);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Connection to service established");
- List<Downloader> downloaderList = downloadService.getDownloads();
- if (downloaderList != null && !downloaderList.isEmpty()) {
- callback.onContentChanged(downloaderList);
- startRefresher();
- }
- }
- };
-
- private void stopRefresher() {
- if (refresherThread != null) {
- refresherThread.interrupt();
- }
- }
-
- private void startRefresher() {
- if (refresherThread == null || refresherThread.isInterrupted()) {
- refresherThread = new Thread(new RefresherThread());
- refresherThread.start();
- }
- }
-
- private class RefresherThread implements Runnable {
-
- public void run() {
- refresherThreadRunning.set(true);
- while (!Thread.interrupted()) {
- try {
- Thread.sleep(WAITING_INTERVAL_MS);
- } catch (InterruptedException e) {
- Log.d(TAG, "Refresher thread was interrupted");
- }
- if (mIsBound.get()) {
- postUpdate();
- }
- }
- refresherThreadRunning.set(false);
- }
-
- private void postUpdate() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- 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>());
- }
- }
- });
- }
- }
-
- public void setActivity(Activity activity) {
- Validate.notNull(activity);
- this.activity = activity;
- }
-
-}
-
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 fea2bbb2b..abb75e5e7 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
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.core.dialog;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import de.danoeh.antennapod.core.R;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java
index 3d174bd8e..b7e79431d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/DownloadRequestErrorDialogCreator.java
@@ -1,8 +1,9 @@
package de.danoeh.antennapod.core.dialog;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
+
import de.danoeh.antennapod.core.R;
/** Creates Alert Dialogs if a DownloadRequestException has happened. */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
new file mode 100644
index 000000000..124fd3e64
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloadEvent.java
@@ -0,0 +1,28 @@
+package de.danoeh.antennapod.core.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.core.service.download.Downloader;
+
+public class DownloadEvent {
+
+ public final DownloaderUpdate update;
+
+ private DownloadEvent(DownloaderUpdate downloader) {
+ this.update = downloader;
+ }
+
+ public static DownloadEvent refresh(List<Downloader> list) {
+ list = new ArrayList<>(list);
+ DownloaderUpdate update = new DownloaderUpdate(list);
+ return new DownloadEvent(update);
+ }
+
+ @Override
+ public String toString() {
+ return "DownloadEvent{" +
+ "update=" + update +
+ '}';
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
new file mode 100644
index 000000000..dcb033267
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
@@ -0,0 +1,53 @@
+package de.danoeh.antennapod.core.event;
+
+import java.util.Arrays;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.util.LongList;
+
+public class DownloaderUpdate {
+
+ /* Downloaders that are currently running */
+ public final List<Downloader> downloaders;
+
+ /**
+ * IDs of feeds that are currently being downloaded
+ * Often used to show some progress wheel in the action bar
+ */
+ public final long[] feedIds;
+
+ /**
+ * IDs of feed media that are currently being downloaded
+ * Can be used to show and update download progress bars
+ */
+ public final long[] mediaIds;
+
+ public DownloaderUpdate(List<Downloader> downloaders) {
+ this.downloaders = downloaders;
+ LongList feedIds1 = new LongList(), mediaIds1 = new LongList();
+ for(Downloader d1 : downloaders) {
+ int type = d1.getDownloadRequest().getFeedfileType();
+ long id = d1.getDownloadRequest().getFeedfileId();
+ if(type == Feed.FEEDFILETYPE_FEED) {
+ feedIds1.add(id);
+ } else if(type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ mediaIds1.add(id);
+ }
+ }
+
+ this.feedIds = feedIds1.toArray();
+ this.mediaIds = mediaIds1.toArray();
+ }
+
+ @Override
+ public String toString() {
+ return "DownloaderUpdate{" +
+ "downloaders=" + downloaders +
+ ", feedIds=" + Arrays.toString(feedIds) +
+ ", mediaIds=" + Arrays.toString(mediaIds) +
+ '}';
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
index a4a79187e..7ff241456 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
@@ -6,6 +6,7 @@ import android.support.annotation.NonNull;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
+import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -32,6 +33,10 @@ public class FeedItemEvent {
return new FeedItemEvent(Action.UPDATE, items);
}
+ public static FeedItemEvent updated(FeedItem... items) {
+ return new FeedItemEvent(Action.UPDATE, Arrays.asList(items));
+ }
+
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java b/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
index 2667a2e12..3425e8a8e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
@@ -26,7 +26,6 @@ public class EventDistributor extends Observable {
public static final int UNREAD_ITEMS_UPDATE = 2;
public static final int DOWNLOADLOG_UPDATE = 8;
public static final int PLAYBACK_HISTORY_UPDATE = 16;
- public static final int DOWNLOAD_QUEUED = 32;
public static final int DOWNLOAD_HANDLED = 64;
public static final int PLAYER_STATUS_UPDATE = 128;
@@ -88,10 +87,6 @@ public class EventDistributor extends Observable {
Validate.isInstanceOf(EventListener.class, observer);
}
- public void sendDownloadQueuedBroadcast() {
- addEvent(DOWNLOAD_QUEUED);
- }
-
public void sendUnreadItemsUpdateBroadcast() {
addEvent(UNREAD_ITEMS_UPDATE);
}
@@ -108,10 +103,6 @@ public class EventDistributor extends Observable {
addEvent(DOWNLOADLOG_UPDATE);
}
- public void sendDownloadHandledBroadcast() {
- addEvent(DOWNLOAD_HANDLED);
- }
-
public void sendPlayerStatusUpdateBroadcast() { addEvent(PLAYER_STATUS_UPDATE); }
public static abstract class EventListener implements Observer {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
index cd865731b..ee13bef25 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
@@ -20,11 +20,10 @@ public class GpodnetTag implements Parcelable {
this.usage = usage;
}
- public static GpodnetTag createFromParcel(Parcel in) {
- final String title = in.readString();
- final String tag = in.readString();
- final int usage = in.readInt();
- return new GpodnetTag(title, tag, usage);
+ protected GpodnetTag(Parcel in) {
+ title = in.readString();
+ tag = in.readString();
+ usage = in.readInt();
}
@Override
@@ -56,5 +55,16 @@ public class GpodnetTag implements Parcelable {
dest.writeInt(usage);
}
+ public static final Creator<GpodnetTag> CREATOR = new Creator<GpodnetTag>() {
+ @Override
+ public GpodnetTag createFromParcel(Parcel in) {
+ return new GpodnetTag(in);
+ }
+
+ @Override
+ public GpodnetTag[] newArray(int size) {
+ return new GpodnetTag[size];
+ }
+ };
}
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 d3c4d3ffd..6de546d3b 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
@@ -565,7 +565,7 @@ public class UserPreferences {
}
public static void setDataFolder(String dir) {
- Log.d(TAG, "Result from DirectoryChooser: " + dir);
+ Log.d(TAG, "setDataFolder(dir: " + dir + ")");
prefs.edit()
.putString(PREF_DATA_FOLDER, dir)
.apply();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
index 9aff7489b..5deb5e501 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.service.download;
-import android.support.annotation.NonNull;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.util.Log;
import com.squareup.okhttp.OkHttpClient;
@@ -18,8 +18,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import de.danoeh.antennapod.core.BuildConfig;
-
/**
* Provides access to a HttpClient singleton.
*/
@@ -151,7 +149,7 @@ public class AntennapodHttpClient {
}
private void configureSocket(SSLSocket s) {
- s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1" } );
+ s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } );
}
}
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 0698107a7..677765e92 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
@@ -53,7 +53,8 @@ import javax.xml.parsers.ParserConfigurationException;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -74,6 +75,7 @@ import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeExceptio
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.InvalidFeedException;
+import de.greenrobot.event.EventBus;
/**
* Manages the download of feedfiles in the app. Downloads can be enqueued viathe startService intent.
@@ -102,12 +104,6 @@ public class DownloadService extends Service {
public static final String EXTRA_DOWNLOAD_URL = "downloadUrl";
/**
- * Sent by the DownloadService when the content of the downloads list
- * changes.
- */
- public static final String ACTION_DOWNLOADS_CONTENT_CHANGED = "action.de.danoeh.antennapod.core.service.downloadsContentChanged";
-
- /**
* Extra for ACTION_ENQUEUE_DOWNLOAD intent.
*/
public static final String EXTRA_REQUEST = "request";
@@ -155,6 +151,8 @@ public class DownloadService extends Service {
private static final int SCHED_EX_POOL_SIZE = 1;
private ScheduledThreadPoolExecutor schedExecutor;
+ private Handler postHandler = new Handler();
+
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
@@ -180,10 +178,7 @@ public class DownloadService extends Service {
final int type = status.getFeedfileType();
if (successful) {
if (type == Feed.FEEDFILETYPE_FEED) {
- handleCompletedFeedDownload(downloader
- .getDownloadRequest());
- } else if (type == FeedImage.FEEDFILETYPE_FEEDIMAGE) {
- handleCompletedImageDownload(status, downloader.getDownloadRequest());
+ handleCompletedFeedDownload(downloader.getDownloadRequest());
} else if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
handleCompletedFeedMediaDownload(status, downloader.getDownloadRequest());
}
@@ -202,9 +197,22 @@ public class DownloadService extends Service {
Log.e(TAG, "Download failed");
saveDownloadStatus(status);
handleFailedDownload(status, downloader.getDownloadRequest());
+
+ // to make lists reload the failed item, we fake an item update
+ if(type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ long id = status.getFeedfileId();
+ FeedMedia media = DBReader.getFeedMedia(id);
+ EventBus.getDefault().post(FeedItemEvent.updated(media.getItem()));
+ }
+ }
+ } else {
+ // if FeedMedia download has been canceled, fake FeedItem update
+ // so that lists reload that it
+ if(status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
+ EventBus.getDefault().post(FeedItemEvent.updated(media.getItem()));
}
}
- sendDownloadHandledIntent();
queryDownloadsAsync();
}
} catch (InterruptedException e) {
@@ -306,6 +314,9 @@ public class DownloadService extends Service {
updateReport();
}
+ postHandler.removeCallbacks(postDownloaderTask);
+ EventBus.getDefault().postSticky(DownloadEvent.refresh(Collections.emptyList()));
+
stopForeground(true);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
@@ -407,15 +418,14 @@ public class DownloadService extends Service {
} else {
Log.e(TAG, "Could not cancel download with url " + url);
}
- sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ postDownloaders();
} else if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
for (Downloader d : downloads) {
d.cancel();
Log.d(TAG, "Cancelled all downloads");
}
- sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
-
+ postDownloaders();
}
queryDownloads();
}
@@ -434,13 +444,14 @@ public class DownloadService extends Service {
if (downloader != null) {
numberOfDownloads.incrementAndGet();
// smaller rss feeds before bigger media files
- if(request.getFeedfileId() == Feed.FEEDFILETYPE_FEED) {
+ if(request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
downloads.add(0, downloader);
} else {
downloads.add(downloader);
}
downloadExecutor.submit(downloader);
- sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+
+ postDownloaders();
}
queryDownloads();
@@ -471,7 +482,7 @@ public class DownloadService extends Service {
boolean rc = downloads.remove(d);
Log.d(TAG, "Result of downloads.remove: " + rc);
DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
- sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
+ postDownloaders();
}
});
}
@@ -487,10 +498,6 @@ public class DownloadService extends Service {
DBWriter.addDownloadStatus(status);
}
- private void sendDownloadHandledIntent() {
- EventDistributor.getInstance().sendDownloadHandledBroadcast();
- }
-
/**
* Creates a notification at the end of the service lifecycle to notify the
* user about the number of completed downloads. A report will only be
@@ -607,14 +614,6 @@ public class DownloadService extends Service {
}
/**
- * Is called whenever a Feed-Image is downloaded
- */
- private void handleCompletedImageDownload(DownloadStatus status, DownloadRequest request) {
- Log.d(TAG, "Handling completed Image Download");
- syncExecutor.execute(new ImageHandlerThread(status, request));
- }
-
- /**
* Is called whenever a FeedMedia is downloaded.
*/
private void handleCompletedFeedMediaDownload(DownloadStatus status, DownloadRequest request) {
@@ -767,8 +766,6 @@ public class DownloadService extends Service {
numberOfDownloads.decrementAndGet();
}
- sendDownloadHandledIntent();
-
queryDownloadsAsync();
}
});
@@ -1013,40 +1010,6 @@ public class DownloadService extends Service {
}
/**
- * Handles a completed image download.
- */
- class ImageHandlerThread implements Runnable {
-
- private DownloadRequest request;
- private DownloadStatus status;
-
- public ImageHandlerThread(DownloadStatus status, DownloadRequest request) {
- Validate.notNull(status);
- Validate.notNull(request);
-
- this.status = status;
- this.request = request;
- }
-
- @Override
- public void run() {
- FeedImage image = DBReader.getFeedImage(request.getFeedfileId());
- if (image == null) {
- throw new IllegalStateException("Could not find downloaded image in database");
- }
-
- image.setFile_url(request.getDestination());
- image.setDownloaded(true);
-
- saveDownloadStatus(status);
- sendDownloadHandledIntent();
- DBWriter.setFeedImage(image);
- numberOfDownloads.decrementAndGet();
- queryDownloadsAsync();
- }
- }
-
- /**
* Handles a completed media download.
*/
class MediaHandlerThread implements Runnable {
@@ -1064,8 +1027,7 @@ public class DownloadService extends Service {
@Override
public void run() {
- FeedMedia media = DBReader.getFeedMedia(
- request.getFeedfileId());
+ FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
if (media == null) {
throw new IllegalStateException(
"Could not find downloaded media object in database");
@@ -1121,7 +1083,6 @@ public class DownloadService extends Service {
}
saveDownloadStatus(status);
- sendDownloadHandledIntent();
if(GpodnetPreferences.loggedIn()) {
FeedItem item = media.getItem();
@@ -1174,16 +1135,24 @@ public class DownloadService extends Service {
}
}
- public List<Downloader> getDownloads() {
- 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>();
+
+ private long lastPost = 0;
+
+ final Runnable postDownloaderTask = new Runnable() {
+ @Override
+ public void run() {
+ List<Downloader> list = Collections.unmodifiableList(downloads);
+ EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
+ postHandler.postDelayed(postDownloaderTask, 1500);
}
+ };
- // return a copy of downloads, but the copy doesn't need to be synchronized.
- synchronized (downloads) {
- return new ArrayList<Downloader>(downloads);
+ private void postDownloaders() {
+ long now = System.currentTimeMillis();
+ if(now - lastPost >= 250) {
+ postHandler.removeCallbacks(postDownloaderTask);
+ postDownloaderTask.run();
+ lastPost = now;
}
}
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 f6c71daa7..b136de47f 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
@@ -16,6 +16,7 @@ import android.support.v4.media.session.PlaybackStateCompat;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Pair;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
@@ -116,9 +117,19 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(context, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mediaSession = new MediaSessionCompat(context, TAG, eventReceiver, buttonReceiverIntent);
- mediaSession.setCallback(sessionCallback);
- mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
- mediaSession.setActive(true);
+
+ try {
+ mediaSession.setCallback(sessionCallback);
+ mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mediaSession.setActive(true);
+ } catch (NullPointerException npe) {
+ // on some devices (Huawei) setting active can cause a NullPointerException
+ // even with correct use of the api.
+ // See http://stackoverflow.com/questions/31556679/android-huawei-mediassessioncompat
+ // and https://plus.google.com/+IanLake/posts/YgdTkKFxz7d
+ Log.e(TAG, "NullPointerException while setting up MediaSession");
+ npe.printStackTrace();
+ }
mediaPlayer = null;
statusBeforeSeeking = null;
@@ -1172,129 +1183,99 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
- @Override
- public void onPlay() {
- if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
- resume();
- } else if (playerStatus == PlayerStatus.INITIALIZED) {
- setStartWhenPrepared(true);
- prepare();
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (playerStatus == PlayerStatus.PLAYING) {
- pause(false, true);
- }
- if (UserPreferences.isPersistNotify()) {
- pause(false, true);
- } else {
- pause(true, true);
- }
- }
-
- @Override
- public void onSkipToNext() {
- super.onSkipToNext();
- endPlayback(true);
- }
-
- @Override
- public void onFastForward() {
- super.onFastForward();
- seekDelta(UserPreferences.getFastFowardSecs() * 1000);
- }
-
- @Override
- public void onRewind() {
- super.onRewind();
- seekDelta(-UserPreferences.getRewindSecs() * 1000);
- }
-
- @Override
- public void onSeekTo(long pos) {
- super.onSeekTo(pos);
- seekTo((int) pos);
- }
+ private static final String TAG = "MediaSessionCompat";
@Override
public boolean onMediaButtonEvent(final Intent mediaButton) {
- Log.d(TAG, "GOT MediaButton EVENT");
+ Log.d(TAG, "onMediaButtonEvent(" + mediaButton + ")");
if (mediaButton != null) {
KeyEvent keyEvent = (KeyEvent) mediaButton.getExtras().get(Intent.EXTRA_KEY_EVENT);
handleMediaKey(keyEvent);
}
- return super.onMediaButtonEvent(mediaButton);
+ return false;
}
};
- public boolean handleMediaKey(KeyEvent event) {
- if (event != null
- && event.getAction() == KeyEvent.ACTION_DOWN
- && event.getRepeatCount() == 0) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- {
- Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
- if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
- resume();
- } else if (playerStatus == PlayerStatus.INITIALIZED) {
- setStartWhenPrepared(true);
- prepare();
- } else if (playerStatus == PlayerStatus.PLAYING) {
- pause(false, true);
- if (UserPreferences.isPersistNotify()) {
- pause(false, true);
- } else {
- pause(true, true);
- }
- }
- return true;
- }
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- {
- Log.d(TAG, "Received Play event from RemoteControlClient");
- if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
- resume();
- } else if (playerStatus == PlayerStatus.INITIALIZED) {
- setStartWhenPrepared(true);
- prepare();
- }
- return true;
- }
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- {
- Log.d(TAG, "Received Pause event from RemoteControlClient");
- if (playerStatus == PlayerStatus.PLAYING) {
- pause(false, true);
- }
+ public boolean handleMediaKey(KeyEvent event) {
+ Log.d(TAG, "handleMediaKey(" + event +")");
+ if (event != null
+ && event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_HEADSETHOOK: {
+ Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
+ if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
+ resume();
+ } else if (playerStatus == PlayerStatus.INITIALIZED) {
+ setStartWhenPrepared(true);
+ prepare();
+ } else if (playerStatus == PlayerStatus.PLAYING) {
+ pause(false, true);
if (UserPreferences.isPersistNotify()) {
pause(false, true);
} else {
pause(true, true);
}
- return true;
}
- case KeyEvent.KEYCODE_MEDIA_STOP:
- {
- Log.d(TAG, "Received Stop event from RemoteControlClient");
- stop();
- return true;
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_PLAY: {
+ Log.d(TAG, "Received Play event from RemoteControlClient");
+ if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
+ resume();
+ } else if (playerStatus == PlayerStatus.INITIALIZED) {
+ setStartWhenPrepared(true);
+ prepare();
}
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- {
- Log.d(TAG, "Received next event from RemoteControlClient");
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_PAUSE: {
+ Log.d(TAG, "Received Pause event from RemoteControlClient");
+ if (playerStatus == PlayerStatus.PLAYING) {
+ pause(false, true);
+ }
+ if (UserPreferences.isPersistNotify()) {
+ pause(false, true);
+ } else {
+ pause(true, true);
+ }
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_STOP: {
+ Log.d(TAG, "Received Stop event from RemoteControlClient");
+ stop();
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS: {
+ seekDelta(-UserPreferences.getRewindSecs() * 1000);
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_REWIND: {
+ seekDelta(-UserPreferences.getRewindSecs() * 1000);
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+ seekDelta(UserPreferences.getFastFowardSecs() * 1000);
+ return true;
+ }
+ case KeyEvent.KEYCODE_MEDIA_NEXT: {
+ if(event.getSource() == InputDevice.SOURCE_CLASS_NONE) {
+ // assume the skip command comes from a notification or the lockscreen
+ // a >| skip button should actually skip
endPlayback(true);
- return true;
+ } else {
+ // assume skip command comes from a (bluetooth) media button
+ // user actually wants to fast-forward
+ seekDelta(UserPreferences.getFastFowardSecs() * 1000);
}
- default:
- Log.d(TAG, "Unhandled key code: " + event.getKeyCode());
- break;
+ return true;
}
+ default:
+ Log.d(TAG, "Unhandled key code: " + event.getKeyCode());
+ break;
}
- return false;
}
+ return false;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index 9c3ff4413..961f923e9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -31,7 +31,7 @@ import de.greenrobot.event.EventBus;
* to notify the PlaybackService about updates from the running tasks.
*/
public class PlaybackServiceTaskManager {
- private static final String TAG = "PlaybackServiceTaskManager";
+ private static final String TAG = "PlaybackServiceTaskMgr";
/**
* Update interval of position saver in milliseconds.
@@ -81,6 +81,7 @@ public class PlaybackServiceTaskManager {
}
public void onEvent(QueueEvent event) {
+ Log.d(TAG, "onEvent(QueueEvent " + event +")");
cancelQueueLoader();
loadQueue();
}
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 a422a3b0c..c0a30f740 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
@@ -3,13 +3,13 @@ package de.danoeh.antennapod.core.storage;
import android.database.Cursor;
import android.util.Log;
-import org.apache.commons.lang3.StringUtils;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
@@ -160,7 +160,7 @@ public final class DBReader {
* The method does NOT change the items-attribute of the feed.
*/
public static List<FeedItem> getFeedItemList(final Feed feed) {
- Log.d(TAG, "Extracting Feeditems of feed " + feed.getTitle());
+ Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -169,11 +169,10 @@ public final class DBReader {
List<FeedItem> items = extractItemlistFromCursor(adapter,
itemlistCursor);
itemlistCursor.close();
+ adapter.close();
Collections.sort(items, new FeedItemPubdateComparator());
- adapter.close();
-
for (FeedItem item : items) {
item.setFeed(feed);
}
@@ -182,6 +181,7 @@ public final class DBReader {
}
public static List<FeedItem> extractItemlistFromCursor(Cursor itemlistCursor) {
+ Log.d(TAG, "extractItemlistFromCursor() called with: " + "itemlistCursor = [" + itemlistCursor + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
List<FeedItem> result = extractItemlistFromCursor(adapter, itemlistCursor);
@@ -189,53 +189,61 @@ public final class DBReader {
return result;
}
- private static List<FeedItem> extractItemlistFromCursor(
- PodDBAdapter adapter, Cursor itemlistCursor) {
- ArrayList<String> itemIds = new ArrayList<>();
- List<FeedItem> items = new ArrayList<>(itemlistCursor.getCount());
+ private static List<FeedItem> extractItemlistFromCursor(PodDBAdapter adapter,
+ Cursor cursor) {
+ List<FeedItem> result = new ArrayList<>(cursor.getCount());
- if (itemlistCursor.moveToFirst()) {
+ LongList imageIds = new LongList(cursor.getCount());
+ LongList itemIds = new LongList(cursor.getCount());
+ if (cursor.moveToFirst()) {
do {
- int indexImage = itemlistCursor.getColumnIndex(PodDBAdapter.KEY_IMAGE);
- long imageId = itemlistCursor.getLong(indexImage);
- FeedImage image = null;
- if (imageId != 0) {
- image = getFeedImage(adapter, imageId);
- }
+ int indexImage = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE);
+ long imageId = cursor.getLong(indexImage);
+ imageIds.add(imageId);
- FeedItem item = FeedItem.fromCursor(itemlistCursor);
+ FeedItem item = FeedItem.fromCursor(cursor);
+ result.add(item);
+ itemIds.add(item.getId());
+ } while (cursor.moveToNext());
+ Map<Long,FeedImage> images = getFeedImages(adapter, imageIds.toArray());
+ Map<Long,FeedMedia> medias = getFeedMedia(adapter, itemIds.toArray());
+ for(int i=0; i < result.size(); i++) {
+ FeedItem item = result.get(i);
+ long imageId = imageIds.get(i);
+ FeedImage image = images.get(imageId);
item.setImage(image);
-
- itemIds.add(String.valueOf(item.getId()));
-
- items.add(item);
- } while (itemlistCursor.moveToNext());
+ FeedMedia media = medias.get(item.getId());
+ item.setMedia(media);
+ if(media != null) {
+ media.setItem(item);
+ }
+ }
}
-
- extractMediafromItemlist(adapter, items, itemIds);
- return items;
+ return result;
}
- private static void extractMediafromItemlist(PodDBAdapter adapter,
- List<FeedItem> items, ArrayList<String> itemIds) {
+ private static Map<Long,FeedMedia> getFeedMedia(PodDBAdapter adapter,
+ long... itemIds) {
- List<FeedItem> itemsCopy = new ArrayList<>(items);
- Cursor cursor = adapter.getFeedMediaCursorByItemID(itemIds
- .toArray(new String[itemIds.size()]));
- if (cursor.moveToFirst()) {
- do {
- int index = cursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM);
- long itemId = cursor.getLong(index);
- // find matching feed item
- FeedItem item = getMatchingItemForMedia(itemId, itemsCopy);
- if (item != null) {
+ ArrayList<String> ids = new ArrayList<>(itemIds.length);
+ for(long itemId : itemIds) {
+ ids.add(String.valueOf(itemId));
+ }
+ Map<Long,FeedMedia> result = new HashMap<>(itemIds.length);
+ Cursor cursor = adapter.getFeedMediaCursor(ids.toArray(new String[0]));
+ try {
+ if (cursor.moveToFirst()) {
+ do {
+ int index = cursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM);
+ long itemId = cursor.getLong(index);
FeedMedia media = FeedMedia.fromCursor(cursor);
- item.setMedia(media);
- item.getMedia().setItem(item);
- }
- } while (cursor.moveToNext());
+ result.put(itemId, media);
+ } while (cursor.moveToNext());
+ }
+ } finally {
+ cursor.close();
}
- cursor.close();
+ return result;
}
private static Feed extractFeedFromCursorRow(PodDBAdapter adapter,
@@ -261,16 +269,6 @@ public final class DBReader {
return feed;
}
- private static FeedItem getMatchingItemForMedia(long itemId,
- List<FeedItem> items) {
- for (FeedItem item : items) {
- if (item.getId() == itemId) {
- return item;
- }
- }
- return null;
- }
-
static List<FeedItem> getQueue(PodDBAdapter adapter) {
Log.d(TAG, "getQueue()");
Cursor itemlistCursor = adapter.getQueueCursor();
@@ -288,6 +286,7 @@ public final class DBReader {
* list in a {@link de.danoeh.antennapod.core.util.QueueAccess} object for easier access to the queue's properties.
*/
public static LongList getQueueIDList() {
+ Log.d(TAG, "getQueueIDList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
LongList result = getQueueIDList(adapter);
@@ -296,7 +295,6 @@ public final class DBReader {
}
static LongList getQueueIDList(PodDBAdapter adapter) {
- adapter.open();
Cursor queueCursor = adapter.getQueueIDCursor();
LongList queueIds = new LongList(queueCursor.getCount());
@@ -317,7 +315,7 @@ public final class DBReader {
* list in a {@link de.danoeh.antennapod.core.util.QueueAccess} object for easier access to the queue's properties.
*/
public static List<FeedItem> getQueue() {
- Log.d(TAG, "getQueue()");
+ Log.d(TAG, "getQueue() called with: " + "");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -332,7 +330,7 @@ public final class DBReader {
* @return A list of FeedItems whose episdoe has been downloaded.
*/
public static List<FeedItem> getDownloadedItems() {
- Log.d(TAG, "Extracting downloaded items");
+ Log.d(TAG, "getDownloadedItems() called with: " + "");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -342,9 +340,10 @@ public final class DBReader {
itemlistCursor);
itemlistCursor.close();
loadAdditionalFeedItemListData(items);
+ adapter.close();
+
Collections.sort(items, new FeedItemPubdateComparator());
- adapter.close();
return items;
}
@@ -355,7 +354,7 @@ public final class DBReader {
* @return A list of FeedItems whose 'read'-attribute it set to false.
*/
public static List<FeedItem> getUnreadItemsList() {
- Log.d(TAG, "Extracting unread items list");
+ Log.d(TAG, "getUnreadItemsList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -376,7 +375,7 @@ public final class DBReader {
* @return A list of FeedItems that are considered new.
*/
public static List<FeedItem> getNewItemsList() {
- Log.d(TAG, "getNewItemsList()");
+ Log.d(TAG, "getNewItemsList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -393,7 +392,7 @@ public final class DBReader {
}
public static List<FeedItem> getFavoriteItemsList() {
- Log.d(TAG, "getFavoriteItemsList()");
+ Log.d(TAG, "getFavoriteItemsList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -410,7 +409,9 @@ public final class DBReader {
}
static LongList getFavoriteIDList() {
- PodDBAdapter adapter = PodDBAdapter.getInstance().open();
+ Log.d(TAG, "getFavoriteIDList() called");
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
Cursor favoritesCursor = adapter.getFavoritesCursor();
LongList favoriteIDs = new LongList(favoritesCursor.getCount());
@@ -420,6 +421,7 @@ public final class DBReader {
} while (favoritesCursor.moveToNext());
}
favoritesCursor.close();
+ adapter.close();
return favoriteIDs;
}
@@ -429,7 +431,7 @@ public final class DBReader {
* @param limit The maximum number of episodes that should be loaded.
*/
public static List<FeedItem> getRecentlyPublishedEpisodes(int limit) {
- Log.d(TAG, "Extracting recently published items list");
+ Log.d(TAG, "getRecentlyPublishedEpisodes() called with: " + "limit = [" + limit + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -453,7 +455,7 @@ public final class DBReader {
* The size of the returned list is limited by {@link #PLAYBACK_HISTORY_SIZE}.
*/
public static List<FeedItem> getPlaybackHistory() {
- Log.d(TAG, "Loading playback history");
+ Log.d(TAG, "getPlaybackHistory() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -482,7 +484,7 @@ public final class DBReader {
* The size of the returned list is limited by {@link #DOWNLOAD_LOG_SIZE}.
*/
public static List<DownloadStatus> getDownloadLog() {
- Log.d(TAG, "Extracting DownloadLog");
+ Log.d(TAG, "getDownloadLog() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -496,6 +498,7 @@ public final class DBReader {
} while (logCursor.moveToNext());
}
logCursor.close();
+ adapter.close();
Collections.sort(downloadLog, new DownloadStatusComparator());
return downloadLog;
}
@@ -508,7 +511,7 @@ public final class DBReader {
* newest events first.
*/
public static List<DownloadStatus> getFeedDownloadLog(Feed feed) {
- Log.d(TAG, "getFeedDownloadLog(" + feed.toString() + ")");
+ Log.d(TAG, "getFeedDownloadLog() called with: " + "feed = [" + feed + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -522,6 +525,7 @@ public final class DBReader {
} while (cursor.moveToNext());
}
cursor.close();
+ adapter.close();
Collections.sort(downloadLog, new DownloadStatusComparator());
return downloadLog;
}
@@ -534,6 +538,7 @@ public final class DBReader {
* @return A list of FeedItemStatistics objects sorted alphabetically by their Feed's title.
*/
public static List<FeedItemStatistics> getFeedStatisticsList() {
+ Log.d(TAG, "getFeedStatisticsList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
List<FeedItemStatistics> result = new ArrayList<>();
@@ -558,6 +563,7 @@ public final class DBReader {
* database and the items-attribute will be set correctly.
*/
public static Feed getFeed(final long feedId) {
+ Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Feed result = getFeed(feedId, adapter);
@@ -566,7 +572,6 @@ public final class DBReader {
}
static Feed getFeed(final long feedId, PodDBAdapter adapter) {
- Log.d(TAG, "Loading feed with id " + feedId);
Feed feed = null;
Cursor feedCursor = adapter.getFeedCursor(feedId);
@@ -635,7 +640,7 @@ public final class DBReader {
* as well as chapter marks of the FeedItem will also be loaded from the database.
*/
public static FeedItem getFeedItem(final long itemId) {
- Log.d(TAG, "Loading feeditem with id " + itemId);
+ Log.d(TAG, "getFeedItem() called with: " + "itemId = [" + itemId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -671,8 +676,7 @@ public final class DBReader {
* as well as chapter marks of the FeedItems will also be loaded from the database.
*/
public static List<FeedItem> getFeedItems(final long... itemIds) {
- Log.d(TAG, "Loading feeditem with ids: " + StringUtils.join(itemIds, ","));
-
+ Log.d(TAG, "getFeedItems() called with: " + "itemIds = [" + itemIds + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
List<FeedItem> items = getFeedItems(adapter, itemIds);
@@ -688,7 +692,7 @@ public final class DBReader {
* @return Credentials in format "<Username>:<Password>", empty String if no authorization given
*/
public static String getImageAuthentication(final String imageUrl) {
- Log.d(TAG, "Loading credentials for image with URL " + imageUrl);
+ Log.d(TAG, "getImageAuthentication() called with: " + "imageUrl = [" + imageUrl + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -728,7 +732,7 @@ public final class DBReader {
* as well as chapter marks of the FeedItem will also be loaded from the database.
*/
public static FeedItem getFeedItem(final String podcastUrl, final String episodeUrl) {
- Log.d(TAG, "Loading feeditem with podcast url " + podcastUrl + " and episode url " + episodeUrl);
+ Log.d(TAG, "getFeedItem() called with: " + "podcastUrl = [" + podcastUrl + "], episodeUrl = [" + episodeUrl + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -743,6 +747,7 @@ public final class DBReader {
* @param item The FeedItem
*/
public static void loadExtraInformationOfFeedItem(final FeedItem item) {
+ Log.d(TAG, "loadExtraInformationOfFeedItem() called with: " + "item = [" + item + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor extraCursor = adapter.getExtraInformationOfItem(item);
@@ -766,6 +771,7 @@ public final class DBReader {
* @param item The FeedItem
*/
public static void loadChaptersOfFeedItem(final FeedItem item) {
+ Log.d(TAG, "loadChaptersOfFeedItem() called with: " + "item = [" + item + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
loadChaptersOfFeedItem(adapter, item);
@@ -820,6 +826,7 @@ public final class DBReader {
* @return The number of downloaded episodes.
*/
public static int getNumberOfDownloadedEpisodes() {
+ Log.d(TAG, "getNumberOfDownloadedEpisodes() called with: " + "");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
final int result = adapter.getNumberOfDownloadedEpisodes();
@@ -834,6 +841,7 @@ public final class DBReader {
* @return The found object
*/
public static FeedImage getFeedImage(final long imageId) {
+ Log.d(TAG, "getFeedImage() called with: " + "imageId = [" + imageId + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
FeedImage result = getFeedImage(adapter, imageId);
@@ -844,21 +852,34 @@ public final class DBReader {
/**
* Searches the DB for a FeedImage of the given id.
*
- * @param id The id of the object
+ * @param imageId The id of the object
* @return The found object
*/
- static FeedImage getFeedImage(PodDBAdapter adapter, final long id) {
- Cursor cursor = adapter.getImageCursor(id);
+ private static FeedImage getFeedImage(PodDBAdapter adapter, final long imageId) {
+ return getFeedImages(adapter, imageId).get(imageId);
+ }
+
+ /**
+ * Searches the DB for a FeedImage of the given id.
+ *
+ * @param ids The id of the object
+ * @return The found object
+ */
+ private static Map<Long,FeedImage> getFeedImages(PodDBAdapter adapter, final long... ids) {
+ Cursor cursor = adapter.getImageCursor(ids);
+ Map<Long, FeedImage> result = new HashMap<>(cursor.getCount());
try {
if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
- return null;
+ return Collections.emptyMap();
}
- FeedImage image = FeedImage.fromCursor(cursor);
- image.setId(id);
- return image;
+ do {
+ FeedImage image = FeedImage.fromCursor(cursor);
+ result.put(image.getId(), image);
+ } while(cursor.moveToNext());
} finally {
cursor.close();
}
+ return result;
}
/**
@@ -897,6 +918,7 @@ public final class DBReader {
* @return The flattr queue as a List.
*/
public static List<FlattrThing> getFlattrQueue() {
+ Log.d(TAG, "getFlattrQueue() called with: " + "");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
List<FlattrThing> result = new ArrayList<>();
@@ -927,6 +949,7 @@ public final class DBReader {
*
*/
public static NavDrawerData getNavDrawerData() {
+ Log.d(TAG, "getNavDrawerData() called with: " + "");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
List<Feed> feeds = getFeedList(adapter);
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 14683506e..49a62da8c 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
@@ -766,6 +766,7 @@ public class DBWriter {
adapter.open();
adapter.setSingleFeedItem(item);
adapter.close();
+ EventBus.getDefault().post(FeedItemEvent.updated(item));
});
}
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 b13f7a0cb..318060abc 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
@@ -15,7 +15,6 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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.FeedMedia;
@@ -85,7 +84,7 @@ public class DownloadRequester {
Intent launchIntent = new Intent(context, DownloadService.class);
launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
context.startService(launchIntent);
- EventDistributor.getInstance().sendDownloadQueuedBroadcast();
+
return true;
}
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 6ccb1d226..c67fb956a 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
@@ -10,14 +10,15 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.media.MediaMetadataRetriever;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.ProgressEvent;
@@ -188,6 +189,15 @@ public class PodDBAdapter {
+ TABLE_NAME_FEED_ITEMS + "_" + KEY_IMAGE + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ KEY_IMAGE + ")";
+ public static final String CREATE_INDEX_FEEDITEMS_PUBDATE = "CREATE INDEX IF NOT EXISTS "
+ + TABLE_NAME_FEED_ITEMS + "_" + KEY_PUBDATE + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ + KEY_PUBDATE + ")";
+
+ public static final String CREATE_INDEX_FEEDITEMS_READ = "CREATE INDEX IF NOT EXISTS "
+ + TABLE_NAME_FEED_ITEMS + "_" + KEY_READ + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ + KEY_READ + ")";
+
+
public static final String CREATE_INDEX_QUEUE_FEEDITEM = "CREATE INDEX "
+ TABLE_NAME_QUEUE + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_QUEUE + " ("
+ KEY_FEEDITEM + ")";
@@ -270,10 +280,10 @@ public class PodDBAdapter {
KEY_CONTENT_ENCODED, KEY_FEED};
- private SQLiteDatabase db;
+ private static SQLiteDatabase db;
private static Context context;
private static PodDBHelper dbHelper;
- private static AtomicInteger counter = new AtomicInteger(0);
+ private static int counter = 0;
public static void init(Context context) {
PodDBAdapter.context = context.getApplicationContext();
@@ -288,12 +298,15 @@ public class PodDBAdapter {
private PodDBAdapter() {}
- public PodDBAdapter open() {
- counter.incrementAndGet();
+ public synchronized PodDBAdapter open() {
+ counter++;
if (db == null || !db.isOpen() || db.isReadOnly()) {
Log.v(TAG, "Opening DB");
try {
db = dbHelper.getWritableDatabase();
+ if(Build.VERSION.SDK_INT >= 11) {
+ db.enableWriteAheadLogging();
+ }
} catch (SQLException ex) {
Log.e(TAG, Log.getStackTraceString(ex));
db = dbHelper.getReadableDatabase();
@@ -302,12 +315,13 @@ public class PodDBAdapter {
return this;
}
- public void close() {
- if(counter.decrementAndGet() == 0) {
+ public synchronized void close() {
+ counter--;
+ if(counter == 0) {
Log.v(TAG, "Closing DB");
db.close();
+ db = null;
}
- db = null;
}
public static boolean deleteDatabase() {
@@ -990,14 +1004,16 @@ public class PodDBAdapter {
}
/**
- * Returns a cursor for a DB query in the FeedImages table for a given ID.
+ * Returns a cursor for a DB query in the FeedImages table for given IDs.
*
- * @param id ID of the FeedImage
+ * @param ids IDs of the FeedImages
* @return The cursor of the query
*/
- public final Cursor getImageCursor(final long id) {
- Cursor c = db.query(TABLE_NAME_FEED_IMAGES, null, KEY_ID + "=?",
- new String[]{String.valueOf(id)}, null, null, null);
+ public final Cursor getImageCursor(long... ids) {
+ String sql = "SELECT * FROM " + TABLE_NAME_FEED_IMAGES +
+ " WHERE " + KEY_ID + " IN (" + StringUtils.join(ids, ',') + ")";
+ Cursor c = db.rawQuery(sql, null);
+
return c;
}
@@ -1138,26 +1154,26 @@ public class PodDBAdapter {
return db.query(TABLE_NAME_FEED_MEDIA, null, KEY_ID + "=?", new String[]{String.valueOf(id)}, null, null, null);
}
- public final Cursor getFeedMediaCursorByItemID(String... mediaIds) {
- int length = mediaIds.length;
+ public final Cursor getFeedMediaCursor(String... itemIds) {
+ int length = itemIds.length;
if (length > IN_OPERATOR_MAXIMUM) {
Log.w(TAG, "Length of id array is larger than "
+ IN_OPERATOR_MAXIMUM + ". Creating multiple cursors");
int numCursors = (int) (((double) length) / (IN_OPERATOR_MAXIMUM)) + 1;
Cursor[] cursors = new Cursor[numCursors];
for (int i = 0; i < numCursors; i++) {
- int neededLength = 0;
- String[] parts = null;
+ int neededLength;
+ String[] parts;
final int elementsLeft = length - i * IN_OPERATOR_MAXIMUM;
if (elementsLeft >= IN_OPERATOR_MAXIMUM) {
neededLength = IN_OPERATOR_MAXIMUM;
- parts = Arrays.copyOfRange(mediaIds, i
+ parts = Arrays.copyOfRange(itemIds, i
* IN_OPERATOR_MAXIMUM, (i + 1)
* IN_OPERATOR_MAXIMUM);
} else {
neededLength = elementsLeft;
- parts = Arrays.copyOfRange(mediaIds, i
+ parts = Arrays.copyOfRange(itemIds, i
* IN_OPERATOR_MAXIMUM, (i * IN_OPERATOR_MAXIMUM)
+ neededLength);
}
@@ -1169,7 +1185,7 @@ public class PodDBAdapter {
return new MergeCursor(cursors);
} else {
return db.query(TABLE_NAME_FEED_MEDIA, null, KEY_FEEDITEM + " IN "
- + buildInOperator(length), mediaIds, null, null, null);
+ + buildInOperator(length), itemIds, null, null, null);
}
}
@@ -1442,7 +1458,7 @@ public class PodDBAdapter {
*/
private static class PodDBHelper extends SQLiteOpenHelper {
- private final static int VERSION = 1040002;
+ private final static int VERSION = 1040013;
private Context context;
@@ -1472,6 +1488,8 @@ public class PodDBAdapter {
db.execSQL(CREATE_INDEX_FEEDITEMS_FEED);
db.execSQL(CREATE_INDEX_FEEDITEMS_IMAGE);
+ db.execSQL(CREATE_INDEX_FEEDITEMS_PUBDATE);
+ db.execSQL(CREATE_INDEX_FEEDITEMS_READ);
db.execSQL(CREATE_INDEX_FEEDMEDIA_FEEDITEM);
db.execSQL(CREATE_INDEX_QUEUE_FEEDITEM);
db.execSQL(CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
@@ -1680,6 +1698,11 @@ public class PodDBAdapter {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ " ADD COLUMN " + PodDBAdapter.KEY_LAST_PLAYED_TIME + " INTEGER DEFAULT 0");
}
+ if(oldVersion < 1040013) {
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_PUBDATE);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_READ);
+ }
+
EventBus.getDefault().post(ProgressEvent.end());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
index 10ffd4bec..6ed8b820e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LongList.java
@@ -177,6 +177,28 @@ public final class LongList {
}
/**
+ * Removes values from this list.
+ *
+ * @param values values to remove
+ */
+ public void removeAll(long[] values) {
+ for(long value : values) {
+ remove(value);
+ }
+ }
+
+ /**
+ * Removes values from this list.
+ *
+ * @param list List with values to remove
+ */
+ public void removeAll(LongList list) {
+ for(long value : list.values) {
+ remove(value);
+ }
+ }
+
+ /**
* Removes an element at a given index, shifting elements at greater
* indicies down one.
*
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 f37933876..318839e1d 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
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.util.flattr;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -8,6 +7,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import org.apache.commons.lang3.StringUtils;
diff --git a/core/src/main/res/values-nb/strings.xml b/core/src/main/res/values-nb/strings.xml
new file mode 100644
index 000000000..59c04afb0
--- /dev/null
+++ b/core/src/main/res/values-nb/strings.xml
@@ -0,0 +1,502 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!--Activitiy and fragment titles-->
+ <string name="app_name">AntennaPod</string>
+ <string name="feeds_label">Strømmer</string>
+ <string name="add_feed_label">Legg til podcast</string>
+ <string name="podcasts_label">PODCASTER</string>
+ <string name="episodes_label">Episoder</string>
+ <string name="new_episodes_label">Nye episoder</string>
+ <string name="all_episodes_label">Alle episoder</string>
+ <string name="all_episodes_short_label">Alle</string>
+ <string name="favorite_episodes_label">Favoritter</string>
+ <string name="new_label">Nye</string>
+ <string name="waiting_list_label">Venteliste</string>
+ <string name="settings_label">Innstillinger</string>
+ <string name="add_new_feed_label">Legg til podcast</string>
+ <string name="downloads_label">Nedlastninger</string>
+ <string name="downloads_running_label">Kjører</string>
+ <string name="downloads_completed_label">Fullført</string>
+ <string name="downloads_log_label">Logg</string>
+ <string name="cancel_download_label">Avbryt\nLast ned</string>
+ <string name="playback_history_label">Avspillingshistorikk</string>
+ <string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_auth_label">gpodder.net-innlogging</string>
+ <!--New episodes fragment-->
+ <string name="recently_published_episodes_label">Nylig publisert</string>
+ <string name="episode_filter_label">Vis kun nye episoder</string>
+ <!--Main activity-->
+ <string name="drawer_open">Åpne menyen</string>
+ <string name="drawer_close">Lukk menyen</string>
+ <string name="drawer_preferences">Skuff-innstillinger</string>
+ <string name="drawer_feed_order_unplayed_episodes">Sorter på teller</string>
+ <string name="drawer_feed_order_alphabetical">Sorter alfabetisk</string>
+ <string name="drawer_feed_order_last_update">Sorter på utgivelsesdato</string>
+ <string name="drawer_feed_counter_new_unplayed">Antall nye og uavspilte episoder</string>
+ <string name="drawer_feed_counter_new">Antall nye episoder</string>
+ <string name="drawer_feed_counter_unplayed">Antall uavspilte episoder</string>
+ <string name="drawer_feed_counter_none">Ingen</string>
+ <!--Webview actions-->
+ <string name="open_in_browser_label">Åpne i nettleser</string>
+ <string name="copy_url_label">Kopier URL</string>
+ <string name="share_url_label">Del URL</string>
+ <string name="copied_url_msg">URL er kopiert til utklippstavlen</string>
+ <string name="go_to_position_label">Gå til denne posisjonen</string>
+ <!--Playback history-->
+ <string name="clear_history_label">Tøm historikk</string>
+ <!--Other-->
+ <string name="confirm_label">Bekreft</string>
+ <string name="cancel_label">Avbryt</string>
+ <string name="yes">Ja</string>
+ <string name="no">Nei</string>
+ <string name="author_label">Opphavsperson</string>
+ <string name="language_label">Språk</string>
+ <string name="url_label">URL</string>
+ <string name="podcast_settings_label">Innstillinger</string>
+ <string name="cover_label">Bilde</string>
+ <string name="error_label">Feil</string>
+ <string name="error_msg_prefix">Det oppsto en feil:</string>
+ <string name="refresh_label">Oppdater</string>
+ <string name="external_storage_error_msg">Finner ikke ekstern lagring. Sjekk at det eksterne lagringsmediet er montert.</string>
+ <string name="chapters_label">Kapittel</string>
+ <string name="shownotes_label">Shownotater</string>
+ <string name="description_label">Beskrivelse</string>
+ <string name="most_recent_prefix">Nyeste episode:\u0020</string>
+ <string name="episodes_suffix">\u0020episoder</string>
+ <string name="length_prefix">Lengde:\u0020</string>
+ <string name="size_prefix">Størrelse:\u0020</string>
+ <string name="processing_label">Behandler</string>
+ <string name="loading_label">Laster opp...</string>
+ <string name="save_username_password_label">Lagre brukernavn og passord</string>
+ <string name="close_label">Lukk</string>
+ <string name="retry_label">Prøv igjen</string>
+ <string name="auto_download_label">Inkluder i automatiske nedlastninger</string>
+ <string name="auto_download_apply_to_items_title">Angi for tidligere episoder</string>
+ <string name="auto_download_apply_to_items_message">Den nye <i>Automatisk nedlasting</i>-innstillingen vil automatisk aktiveres for nye episoder.\nØnsker du å aktivere den for tidligere utgitte episoder også?</string>
+ <string name="auto_delete_label">Automatisk sletting av episode\n(overstyr den globale standarden)</string>
+ <string name="parallel_downloads_suffix">\u0020samtidige nedlastinger</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Alltid</string>
+ <string name="feed_auto_download_never">Aldri</string>
+ <string name="send_label">Send ...</string>
+ <string name="episode_cleanup_never">AldriAldri</string>
+ <string name="episode_cleanup_queue_removal">Når ikke i kø</string>
+ <string name="episode_cleanup_after_listening">Etter den er ferdig</string>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="one">1 dag etter fullført avspilling</item>
+ <item quantity="other">%d dager etter fullført avspilling</item>
+ </plurals>
+ <!--'Add Feed' Activity labels-->
+ <string name="feedurl_label">Strømmens URL</string>
+ <string name="etxtFeedurlHint">www.example.com/feed</string>
+ <string name="txtvfeedurl_label">Legg til en podcast via URL</string>
+ <string name="podcastdirectories_label">Finn podcast i katalog</string>
+ <string name="podcastdirectories_descr">Du kan søke etter nye podcaster på navn, kategori eller popularitet i gpodder.nets katalog eller på iTunes.</string>
+ <string name="browse_gpoddernet_label">Bla gjennom gpodder.net</string>
+ <!--Actions on feeds-->
+ <string name="mark_all_read_label">Marker alle som avspilt</string>
+ <string name="mark_all_read_msg">Marker alle episoder som avspilt</string>
+ <string name="mark_all_read_confirmation_msg">Vennligst bekreft at du ønsker å markere alle episoder som avspilt.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Vennligst bekreft at du ønsker å markere alle episoder i denne strømmen som avspilt.</string>
+ <string name="mark_all_seen_label">Marker alle som sett</string>
+ <string name="show_info_label">Vis informasjon</string>
+ <string name="remove_feed_label">Fjern podcast</string>
+ <string name="share_label">Del ...</string>
+ <string name="share_link_label">Del lenke</string>
+ <string name="share_link_with_position_label">Del lenke med plassering</string>
+ <string name="share_feed_url_label">Del strømmens URL</string>
+ <string name="share_item_url_label">Del episodens URL</string>
+ <string name="share_item_url_with_position_label">Del episodens URL med posisjon</string>
+ <string name="feed_delete_confirmation_msg">Vil du virkelig slette denne strømmen og alle episodene av denne strømmen du har lastet ned?</string>
+ <string name="feed_remover_msg">Fjerner strøm</string>
+ <string name="load_complete_feed">Oppdater hele strømmen</string>
+ <string name="hide_episodes_title">Skjul episoder</string>
+ <string name="episode_actions">Lagre handlinger</string>
+ <string name="hide_unplayed_episodes_label">Ikke avspilt</string>
+ <string name="hide_paused_episodes_label">Pauset</string>
+ <string name="hide_played_episodes_label">Avspilt</string>
+ <string name="hide_queued_episodes_label">I kø</string>
+ <string name="hide_not_queued_episodes_label">Ikke i kø</string>
+ <string name="hide_downloaded_episodes_label">Nedlastet</string>
+ <string name="hide_not_downloaded_episodes_label">Ikke nedlastet</string>
+ <string name="filtered_label">Filtrert</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Siste oppdatering mislyktes</string>
+ <!--actions on feeditems-->
+ <string name="download_label">Last ned</string>
+ <string name="play_label">Spill</string>
+ <string name="pause_label">Pause</string>
+ <string name="stop_label">Stopp</string>
+ <string name="stream_label">Stream</string>
+ <string name="remove_label">Fjern</string>
+ <string name="remove_episode_lable">Fjern episode</string>
+ <string name="mark_read_label">Marker som avspilt</string>
+ <string name="marked_as_read_label">Marker som avspilt</string>
+ <string name="mark_unread_label">Marker som ikke avspilt</string>
+ <string name="add_to_queue_label">Legg til queue</string>
+ <string name="added_to_queue_label">Lagt til i kø</string>
+ <string name="remove_from_queue_label">Fjern fra queue</string>
+ <string name="add_to_favorite_label">Legg til i favoritter</string>
+ <string name="remove_from_favorite_label">Fjern fra favoritter</string>
+ <string name="visit_website_label">Besøk nettside</string>
+ <string name="support_label">Flattr\'e dette</string>
+ <string name="enqueue_all_new">Legg alle til queue</string>
+ <string name="download_all">Last ned alle</string>
+ <string name="skip_episode_label">Skip episode</string>
+ <string name="activate_auto_download">Aktiver automatisk nedlasting</string>
+ <string name="deactivate_auto_download">Deaktiver automatisk nedlasting</string>
+ <string name="reset_position">Tilbakestill avspillingsposisjon</string>
+ <string name="removed_item">Element fjernet</string>
+ <!--Download messages and labels-->
+ <string name="download_successful">nedlastning lyktes</string>
+ <string name="download_failed">mislyktes</string>
+ <string name="download_pending">Nedlastning venter</string>
+ <string name="download_running">Nedlasting pågår</string>
+ <string name="download_error_device_not_found">Lagringsenhet ikke funnet</string>
+ <string name="download_error_insufficient_space">Ikke nok plass</string>
+ <string name="download_error_file_error">Fil-feil</string>
+ <string name="download_error_http_data_error">HTTP-datafeil</string>
+ <string name="download_error_error_unknown">Ukjent feil</string>
+ <string name="download_error_parser_exception">Parser-unntak</string>
+ <string name="download_error_unsupported_type">Strøm-typen er ikke støttet</string>
+ <string name="download_error_connection_error">Tilkoblingsfeil</string>
+ <string name="download_error_unknown_host">Ukjent vert</string>
+ <string name="download_error_unauthorized">Autentiseringsfeil</string>
+ <string name="cancel_all_downloads_label">Avbryt alle nedlastninger</string>
+ <string name="download_canceled_msg">Nedlasting avbrutt</string>
+ <string name="download_canceled_autodownload_enabled_msg">Nedlasting avbrutt\n<i>Automatisk nedlasting</i> for dette elementet er deaktivert</string>
+ <string name="download_report_title">Nedlasting fullført med feilmeldinger</string>
+ <string name="download_report_content_title">Nedlastingsrapport</string>
+ <string name="download_error_malformed_url">Misformet URL</string>
+ <string name="download_error_io_error">IO feil</string>
+ <string name="download_error_request_error">Forespørselfeil</string>
+ <string name="download_error_db_access">Tilgangsfeil for database</string>
+ <string name="downloads_left">\u0020Nedlastninger igjen</string>
+ <string name="downloads_processing">Behandler nedlastninger</string>
+ <string name="download_notification_title">Laster ned data til podcast</string>
+ <string name="download_report_content">%1$d nedlastninger lyktes, %2$d mislyktes</string>
+ <string name="download_log_title_unknown">Ukjent tittel</string>
+ <string name="download_type_feed">Strøm</string>
+ <string name="download_type_media">Mediafil</string>
+ <string name="download_type_image">Bilde</string>
+ <string name="download_request_error_dialog_message_prefix">En feil oppsto under nedlastningen av filen:\u0020</string>
+ <string name="authentication_notification_title">Autentisering påkreves</string>
+ <string name="authentication_notification_msg">Ressursen du forespør krever brukernavn og passord</string>
+ <string name="confirm_mobile_download_dialog_title">Bekreft nedlasting over mobildata</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Nedlasting over mobildata er deaktivert i innstillingene.\n\nDu kan velge å legge episoden til i køen eller tillate midlertidig nedlasting over mobildata.\n\n<small>Valget ditt vil gjelde i 10 minutter.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Nedlasting over mobildata er deaktivert i innstillingene.\n\nVil du tillate nedlasting over mobildata midlertidig?\n\n<small>Valget ditt vil gjelde i 10 minutter.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Legg til i kø</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Tillat midlertidig</string>
+ <!--Mediaplayer messages-->
+ <string name="player_error_msg">Error!</string>
+ <string name="player_stopped_msg">Ingen media spillende for øyeblikket.</string>
+ <string name="player_preparing_msg">Forbereder</string>
+ <string name="player_ready_msg">Klar</string>
+ <string name="player_seeking_msg">Oppsøker</string>
+ <string name="playback_error_server_died">Serveren døde</string>
+ <string name="playback_error_unknown">Ukjent feil</string>
+ <string name="no_media_playing_label">Ingen media spillende for øyeblikket.</string>
+ <string name="position_default_label">00:00:00</string>
+ <string name="player_buffering_msg">Bufrer</string>
+ <string name="playbackservice_notification_title">Spiller podcast</string>
+ <string name="unknown_media_key">AntennaPod - Ukjent medienøkkel: %1$d</string>
+ <!--Queue operations-->
+ <string name="lock_queue">Lås køen</string>
+ <string name="unlock_queue">Lås opp køen</string>
+ <string name="clear_queue_label">Slett køen</string>
+ <string name="undo">Angre</string>
+ <string name="removed_from_queue">Objekt er fjernet</string>
+ <string name="move_to_top_label">Gå til toppen</string>
+ <string name="move_to_bottom_label">Gå til bunnen</string>
+ <string name="sort">Sortér</string>
+ <string name="alpha">Alfabetisk</string>
+ <string name="date">På dato</string>
+ <string name="duration">På varighet</string>
+ <string name="ascending">Økende</string>
+ <string name="descending">Synkende</string>
+ <string name="clear_queue_confirmation_msg">Vennligst bekreft at du ønsker å slette ALLE elementer i køen</string>
+ <!--Flattr-->
+ <string name="flattr_auth_label">Flattr innlogging</string>
+ <string name="flattr_auth_explanation">Trykk knappen nedenfor for å starte autentiseringsprosessen. Du vil videresendes til flattr sin innloggsinsskjerm i din nettleser og spurt om å gi AntennaPod tillatelse til å flattr\'e ting. Etter at du har gitt tillatelse blir du returnert hit automatisk.</string>
+ <string name="authenticate_label">Autentiser</string>
+ <string name="return_home_label">Returner hjem</string>
+ <string name="flattr_auth_success">Autentisering fullført! Nå kan du flattr tingene i denne appen.</string>
+ <string name="no_flattr_token_title">Flattr-token ikke funnet</string>
+ <string name="no_flattr_token_notification_msg">Det virker som at Flattr-kontoen din ikke er sammenkoblet med AntennaPod. Trykk her for å autentisere.</string>
+ <string name="no_flattr_token_msg">Det virker ikke som din flattr konto er koblet til AntennaPod. Du kan enten koble kontoen din til AntennaPod for å flattr\'e ting i appen eller du kan besøke nettsiden til tingen for å flattr\'e det der.</string>
+ <string name="authenticate_now_label">Autentiser</string>
+ <string name="action_forbidden_title">Handling forbudt</string>
+ <string name="action_forbidden_msg">AntennaPod har ikke tilgang til denne handlingen. Grunnen kan være at tilgangstokenet til kontoen din er blitt inndratt. Du kan enten re-autentisere eller besøke tjenestens nettsted.</string>
+ <string name="access_revoked_title">Tilgang opphevet</string>
+ <string name="access_revoked_info">Du har fjernet AntennaPods tilgang til kontoen din. For å fullføre prossessen må du fjerne denne appen fra listen over tillatte apper på flattr-nettsiden.</string>
+ <!--Flattr-->
+ <string name="flattr_click_success">Flattr\'erte en ting!</string>
+ <string name="flattr_click_success_count">Flattret %d ting!</string>
+ <string name="flattr_click_success_queue">Flattret: %s.</string>
+ <string name="flattr_click_failure_count">Klarte ikke flattre %d ting!</string>
+ <string name="flattr_click_failure">Ikke flattret: %s.</string>
+ <string name="flattr_click_enqueued">Tingen vil bli flattret senere</string>
+ <string name="flattring_thing">Flattrer %s</string>
+ <string name="flattring_label">AntennaPod flattrer</string>
+ <string name="flattrd_label">AntennaPod har flattret</string>
+ <string name="flattrd_failed_label">AntennaPods flattring feilet</string>
+ <string name="flattr_retrieving_status">Henter flattrede ting</string>
+ <!--Variable Speed-->
+ <string name="download_plugin_label">Last ned programtillegg</string>
+ <string name="no_playback_plugin_title">Programtillegg er ikke installert</string>
+ <string name="no_playback_plugin_or_sonic_msg">For at variabel avspillingshastighet skal fungere må du installere et tredjepartsbibliotek eller aktivere den eksperimentelle Sonic-avspilleren [Android 4.1+].\n\nTrykk på «Last ned programtillegg» for å laste ned en gratis tilleggsmodul fra Google Play.\n\nEventuelle problemer som måtte oppstå på grunn av denne modulen er ikke AntennaPods ansvar og skal rapporteres til eieren av modulen.</string>
+ <string name="set_playback_speed_label">Avspillingshastigheter</string>
+ <string name="enable_sonic">Skru på Sonic</string>
+ <!--Empty list labels-->
+ <string name="no_items_label">Det er ingen objekter på denne listen.</string>
+ <string name="no_feeds_label">Du abonnerer ikke på noen strømmer enda.</string>
+ <!--Preferences-->
+ <string name="other_pref">Annet</string>
+ <string name="about_pref">Om</string>
+ <string name="queue_label">Queue</string>
+ <string name="services_label">Tjenester</string>
+ <string name="flattr_label">Flattr</string>
+ <string name="pref_episode_cleanup_title">Episodeopprydding</string>
+ <string name="pref_episode_cleanup_summary">Episoder som ikke er i køen og ikke er merket som favoritt vil være markert for sletting dersom lagringsplass trengs</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Gjenoppta avspilling når hodetelefoner gjeninnkoples</string>
+ <string name="pref_followQueue_sum">Hopp til neste element i køen når avspillingen er ferdig</string>
+ <string name="pref_auto_delete_sum">Slett episode når avspillingen er ferdig</string>
+ <string name="pref_auto_delete_title">Automatisk sletting</string>
+ <string name="pref_smart_mark_as_played_sum">Marker episoder som avspilt selv om det er X antall sekunder igjen av avspillingen</string>
+ <string name="pref_smart_mark_as_played_title">Smart markering av avspilt</string>
+ <string name="pref_skip_keeps_episodes_sum">Behold episoder når de hoppes over</string>
+ <string name="pref_skip_keeps_episodes_title">Behold episoder som er hoppet over</string>
+ <string name="playback_pref">Avspilling</string>
+ <string name="network_pref">Nettverk</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Oppdateringsintervall eler tidspunkt</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Spesifiser en intervall eller et spesifikt tidspunkt når strømmer skal oppdateres automatisk</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Du kan sette en <i>intervall</i> som «hver andre time», et <i>spesifikt tidspunkt</i> som «07:00» eller <i>skru av</i> automatiske oppdateringer helt.\n\n<small>Merk: Oppdateringstider er ikke eksakte; du kan oppleve små forsinkelser.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Skru av</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Sett intervall</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Angi klokkeslett</string>
+ <string name="pref_downloadMediaOnWifiOnly_sum">Last ned mediafiler eksklusivt med WiFi</string>
+ <string name="pref_followQueue_title">Kontinuerlig avspilling</string>
+ <string name="pref_downloadMediaOnWifiOnly_title">WiFi media nedlastning</string>
+ <string name="pref_pauseOnHeadsetDisconnect_title">Frakobling av hodetelefoner</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Gjeninnkopling av hodetelefoner</string>
+ <string name="pref_mobileUpdate_title">Mobiloppdateringer</string>
+ <string name="pref_mobileUpdate_sum">Tillat oppdateringer over kobling via mobildata</string>
+ <string name="refreshing_label">Oppdaterer</string>
+ <string name="flattr_settings_label">Flattr innstillinger</string>
+ <string name="pref_flattr_auth_title">Flattr logg in</string>
+ <string name="pref_flattr_auth_sum">Logg på din flattr konto for å flattr ting direkte fra appen.</string>
+ <string name="pref_flattr_this_app_title">Flattr denne appen</string>
+ <string name="pref_flattr_this_app_sum">Støtt utviklingen av AntennaPod ved å flattr\'e det. Tusen takk!</string>
+ <string name="pref_revokeAccess_title">Opphev tilgang</string>
+ <string name="pref_revokeAccess_sum">Opphev tilgangstillatelsen til din flattr konto for denne appen.</string>
+ <string name="pref_auto_flattr_title">Automatisk Flattr</string>
+ <string name="pref_auto_flattr_sum">Konfigurer automatisk flattring</string>
+ <string name="user_interface_label">Brukergrensesnitt</string>
+ <string name="pref_set_theme_title">Velg tema</string>
+ <string name="pref_nav_drawer_title">Skreddersy navigasjonsskuff</string>
+ <string name="pref_nav_drawer_sum">Velg utseendet til navigasjonsskuffen.</string>
+ <string name="pref_nav_drawer_items_title">Velg elementer i navigasjonsskuffen</string>
+ <string name="pref_nav_drawer_items_sum">Endre hvilke elementer som vises i navigeringsfanen.</string>
+ <string name="pref_nav_drawer_feed_order_title">Velg rekkefølge på abbonement</string>
+ <string name="pref_nav_drawer_feed_order_sum">Endre rekkefølgen på abbonementene dine</string>
+ <string name="pref_nav_drawer_feed_counter_title">Velg abbonementsteller</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Endre informasjonen vist av abonnementstelleren</string>
+ <string name="pref_set_theme_sum">Endre utseendet til AntennaPod</string>
+ <string name="pref_automatic_download_title">Automatisk nedlasting</string>
+ <string name="pref_automatic_download_sum">Konfigurer automatisk nedlasting av episoder.</string>
+ <string name="pref_autodl_wifi_filter_title">Skru på Wi-Fi-filter</string>
+ <string name="pref_autodl_wifi_filter_sum">Tillat automatisk nedlasting kun for valgte Wi-Fi-nettverk.</string>
+ <string name="pref_automatic_download_on_battery_title">Last ned når enheten ikke lader</string>
+ <string name="pref_automatic_download_on_battery_sum">Tillat automatisk nedlasting når enheten ikke står til lading</string>
+ <string name="pref_parallel_downloads_title">Parallelle nedlastinger</string>
+ <string name="pref_episode_cache_title">Mellomlager for episoder</string>
+ <string name="pref_theme_title_light">Lyst</string>
+ <string name="pref_theme_title_dark">Mørkt</string>
+ <string name="pref_episode_cache_unlimited">Ulimitert</string>
+ <string name="pref_update_interval_hours_plural">Timer</string>
+ <string name="pref_update_interval_hours_singular">Time</string>
+ <string name="pref_update_interval_hours_manual">Manuelt</string>
+ <string name="pref_gpodnet_authenticate_title">Logg inn</string>
+ <string name="pref_gpodnet_authenticate_sum">Logg inn med din gpodder.net konto for å synkronisere dine abonnementer.</string>
+ <string name="pref_gpodnet_logout_title">Logg ut</string>
+ <string name="pref_gpodnet_logout_toast">Utloggelse lyktes</string>
+ <string name="pref_gpodnet_setlogin_information_title">Endre innloggingsinformasjon</string>
+ <string name="pref_gpodnet_setlogin_information_sum">Endre innlogginsinformasjonen til din gpodder.net konto</string>
+ <string name="pref_playback_speed_title">Avspillingshastigheter</string>
+ <string name="pref_playback_speed_sum">Egendefiner hastighetene tilgjengelig for variabel avspillingshastighet</string>
+ <string name="pref_fast_forward">Spoling fremover</string>
+ <string name="pref_rewind">Spoling bakover</string>
+ <string name="pref_gpodnet_sethostname_title">Sett vertsnavn</string>
+ <string name="pref_gpodnet_sethostname_use_default_host">Bruk standard vert</string>
+ <string name="pref_expandNotify_title">Utvid varsel</string>
+ <string name="pref_expandNotify_sum">Utvider alltid varselet for å inkludere avspillingsknapper.</string>
+ <string name="pref_persistNotify_title">Vedvarende avspillingskontroller</string>
+ <string name="pref_persistNotify_sum">Behold varsel- og låseskjermkontroller når avspilling er satt på pause.</string>
+ <string name="pref_lockscreen_background_title">Angi som bakgrunn på låseskjermen</string>
+ <string name="pref_lockscreen_background_sum">Angir låseskjermbakgrunnsbildet til å være den nåværende episodens bilde. Som en sideeffekt vil dette også vise bildet i tredjepartsapper.</string>
+ <string name="pref_showDownloadReport_title">Vis nedlastingsrapport</string>
+ <string name="pref_showDownloadReport_sum">Generer en rapport som viser detaljer dersom nedlastinger feiler.</string>
+ <string name="pref_expand_notify_unsupport_toast">Android-versjoner tidligere enn 4.1 støtter ikke utvidede varsler.</string>
+ <string name="pref_queueAddToFront_sum">Legg til nye episoder i begynnelsen av køen.</string>
+ <string name="pref_queueAddToFront_title">Legg til foran i køen</string>
+ <string name="pref_smart_mark_as_played_disabled">Deaktivert</string>
+ <string name="pref_image_cache_size_title">Størrelse for bildemellomlager</string>
+ <string name="pref_image_cache_size_sum">Størrelsen på mellomlageret for bilder.</string>
+ <string name="experimental_pref">Eksperimentell</string>
+ <string name="pref_sonic_title">Sonic medieavspiller</string>
+ <string name="pref_sonic_message">Bruk den innebygde Sonic medieavspilleren som en erstatning for Prestissimo</string>
+ <!--Auto-Flattr dialog-->
+ <string name="auto_flattr_enable">Aktiver automatisk flattring</string>
+ <string name="auto_flattr_after_percent">Flattre episode så snart %d prosent er avspilt</string>
+ <string name="auto_flattr_ater_beginning">Flattre episode når avspillingen starter</string>
+ <string name="auto_flattr_ater_end">Flattre episode når avspillingen tar slutt</string>
+ <!--Search-->
+ <string name="search_hint">Søk etter strømmer eller episoder</string>
+ <string name="found_in_shownotes_label">Funnet i shownotater</string>
+ <string name="found_in_chapters_label">Funnet i kapitler</string>
+ <string name="search_status_no_results">Ingen resultater ble funnet</string>
+ <string name="search_label">Søk</string>
+ <string name="found_in_title_label">Funnet i tittel</string>
+ <!--OPML import and export-->
+ <string name="opml_import_txtv_button_lable">OPML-filer lar deg flytte podcastene dine fra en podcatcher til en annen.</string>
+ <string name="opml_import_option">Valg %1$d</string>
+ <string name="opml_import_explanation_1">Velg en spesifikk filbane fra det lokale filsystemet.</string>
+ <string name="opml_import_explanation_2">Bruk en ektern applikasjon som Dropbox, Google Drive eller favoritt-filbehandleren din for å åpne en OPML-fil.</string>
+ <string name="opml_import_explanation_3">Mange applikasjoner som Gmail, Dropbox, Google Drive og de fleste filbehandlere kan <i>åpne</i> OPML-filer <i>med</i> AntennaPod.</string>
+ <string name="start_import_label">Start importering</string>
+ <string name="opml_import_label">OPML-import</string>
+ <string name="opml_directory_error">ERROR!</string>
+ <string name="reading_opml_label">Leser OPML-fil</string>
+ <string name="opml_reader_error">En feil oppsto under lesingen av opml dokumentet:</string>
+ <string name="opml_import_error_dir_empty">Importkatalogen er tom.</string>
+ <string name="select_all_label">Velg alle</string>
+ <string name="deselect_all_label">Opphev alle markeringene</string>
+ <string name="select_options_label">Velg ...</string>
+ <string name="choose_file_from_filesystem">Fra lokalt filsystem</string>
+ <string name="choose_file_from_external_application">Bruk ekstern applikasjon</string>
+ <string name="opml_export_label">OPML-eksportering</string>
+ <string name="exporting_label">Eksporterer...</string>
+ <string name="export_error_label">Eksporteringserror</string>
+ <string name="opml_export_success_title">OPML-import vellykket.</string>
+ <string name="opml_export_success_sum">.opml-filen ble skrevet til:\u0020</string>
+ <!--Sleep timer-->
+ <string name="set_sleeptimer_label">Sett opp sovetimer</string>
+ <string name="disable_sleeptimer_label">Deaktiver sovetimer</string>
+ <string name="enter_time_here_label">Legg til tid</string>
+ <string name="sleep_timer_label">Sovetimer</string>
+ <string name="time_left_label">Tid igjen:\u0020</string>
+ <string name="time_dialog_invalid_input">Ugyldig innspill, tid må være et heltall</string>
+ <string name="timer_about_to_expire_label"><b>Når nedtellingen er i ferd med å utløpe:</b></string>
+ <string name="shake_to_reset_label">Rist for å tilbakestille nedtellingen</string>
+ <string name="timer_vibration_label">Vibrer</string>
+ <string name="time_seconds">sekunder</string>
+ <string name="time_minutes">minutter</string>
+ <string name="time_hours">timer</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 minutt</item>
+ <item quantity="other">%d minutter</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 time</item>
+ <item quantity="other">%d timer</item>
+ </plurals>
+ <!--gpodder.net-->
+ <string name="gpodnet_taglist_header">KATEGORIER</string>
+ <string name="gpodnet_toplist_header">TOPP-PODCASTER</string>
+ <string name="gpodnet_suggestions_header">FORSLAG</string>
+ <string name="gpodnet_search_hint">Søk på gpodder.net</string>
+ <string name="gpodnetauth_login_title">Logg inn</string>
+ <string name="gpodnetauth_login_descr">Velkommen til gpodder.net innlogginsprosess. Først begynner vi med å skrive inn innlogginsinformasjon.</string>
+ <string name="gpodnetauth_login_butLabel">Logg inn</string>
+ <string name="gpodnetauth_login_register">Dersom du ikke har en konto enda kan du opprette en her:\nhttps://gpodder.net/register/</string>
+ <string name="username_label">Brukernavn</string>
+ <string name="password_label">Passord</string>
+ <string name="gpodnetauth_device_title">Enhetsvalg</string>
+ <string name="gpodnetauth_device_descr">Lag en ny enhet til å bruke for din gpodder.net konto eller velg en som allerede eksisterer.</string>
+ <string name="gpodnetauth_device_deviceID">EnhetsID:\u0020</string>
+ <string name="gpodnetauth_device_caption">Tekst</string>
+ <string name="gpodnetauth_device_butCreateNewDevice">Lag en ny enhet</string>
+ <string name="gpodnetauth_device_chooseExistingDevice">Velg eksisterende enhet:</string>
+ <string name="gpodnetauth_device_errorEmpty">Device ID kan ikke være tom</string>
+ <string name="gpodnetauth_device_errorAlreadyUsed">EnhetsID er allerede i bruk</string>
+ <string name="gpodnetauth_device_butChoose">Velg</string>
+ <string name="gpodnetauth_finish_title">Innlogging lyktes!</string>
+ <string name="gpodnetauth_finish_descr">Gratulerer! Din gpodder.net konto er nå linket opp med din enhet. AntennaPod vil nå automatisk synkronisere abonnementer på din enhet med din gpodder.net konto.</string>
+ <string name="gpodnetauth_finish_butsyncnow">Start synkronisering nå.</string>
+ <string name="gpodnetauth_finish_butgomainscreen">Gå til hovedskjermen</string>
+ <string name="gpodnetsync_auth_error_title">gpodder.net-autentiseringsfeil</string>
+ <string name="gpodnetsync_auth_error_descr">Feil brukernavn eller passord.</string>
+ <string name="gpodnetsync_error_title">gpodder.net synkroniseringserror</string>
+ <string name="gpodnetsync_error_descr">En feil oppsto under synkronisering av:\u0020</string>
+ <!--Directory chooser-->
+ <string name="selected_folder_label">Valgt mappe</string>
+ <string name="create_folder_label">Lag mappe</string>
+ <string name="choose_data_directory">Velg datamappe</string>
+ <string name="create_folder_msg">Lag en ny mappe med navn \"%1$s\"?</string>
+ <string name="create_folder_success">Lagde en ny mappe</string>
+ <string name="create_folder_error_no_write_access">Kan ikke skrive til denne mappen</string>
+ <string name="create_folder_error_already_exists">Mappe eksisterer allerede</string>
+ <string name="create_folder_error">Kunne ikke lage mappe</string>
+ <string name="folder_not_empty_dialog_title">Mappen er ikke tom</string>
+ <string name="folder_not_empty_dialog_msg">Mappen du har valgt er ikke tom. Nedlastet media og andre filer vil bli plassert direkte i denne mappen? Vil du fortsette?</string>
+ <string name="set_to_default_folder">Velg standardmappe</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Sett pause på playback istedenfor å skru ned volumet når en annen app har lyst å spille av lyder</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Pause for avbrytelser</string>
+ <string name="pref_resumeAfterCall_sum">Gjenoppta avspilling etter at telefonsamtaler avsluttes</string>
+ <string name="pref_resumeAfterCall_title">Gjenoppta etter samtale</string>
+ <string name="pref_restart_required">AntennaPod må startes om for at denne endringen skal lagres.</string>
+ <!--Online feed view-->
+ <string name="subscribe_label">Abonner</string>
+ <string name="subscribed_label">Abonnert</string>
+ <string name="downloading_label">Laster ned...</string>
+ <!--Content descriptions for image buttons-->
+ <string name="show_chapters_label">Vis kapitler</string>
+ <string name="show_shownotes_label">Vis notater</string>
+ <string name="show_cover_label">Vis bilde</string>
+ <string name="rewind_label">Spol tilbake</string>
+ <string name="fast_forward_label">Spol fremover</string>
+ <string name="media_type_audio_label">Lyd</string>
+ <string name="media_type_video_label">Video</string>
+ <string name="navigate_upwards_label">Naviger oppover</string>
+ <string name="butAction_label">Flere handlinger</string>
+ <string name="status_playing_label">Episode spilles nå</string>
+ <string name="status_downloading_label">Episode lastes ned nå</string>
+ <string name="status_downloaded_label">Episode har blitt lastet ned</string>
+ <string name="status_unread_label">Objekt er nytt</string>
+ <string name="in_queue_label">Episoden er i queue</string>
+ <string name="new_episodes_count_label">Antall nye episoder</string>
+ <string name="in_progress_episodes_count_label">Antall episoder du har begynt å høre på</string>
+ <string name="drag_handle_content_description">Dra for å endre posisjonen til dette objektet</string>
+ <string name="load_next_page_label">Last inn neste side</string>
+ <!--Feed information screen-->
+ <string name="authentication_label">Autentisering</string>
+ <string name="authentication_descr">Endre brukernavnet og passordet for denne podcasten og dens episoder.</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Oppgraderer databasen</string>
+ <!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Importerer abbonementer fra enkeltstående applikasjoner ...</string>
+ <string name="search_itunes_label">Søk på iTunes</string>
+ <string name="select_label"><b>Velg ...</b></string>
+ <string name="all_label">Alle</string>
+ <string name="selected_all_label">Valgte alle episoder</string>
+ <string name="none_label">Ingen</string>
+ <string name="deselected_all_label">Fjernet valg av alle episoder</string>
+ <string name="played_label">Avspilt</string>
+ <string name="selected_played_label">Valgte avspilte episoder</string>
+ <string name="unplayed_label">Uavspilt</string>
+ <string name="selected_unplayed_label">Valgte uavspilte episoder</string>
+ <string name="downloaded_label">Nedlastede</string>
+ <string name="selected_downloaded_label">Valgte nedlastede episoder</string>
+ <string name="not_downloaded_label">Ikke nedlastet</string>
+ <string name="selected_not_downloaded_label">Valgte ikke-nedlastede episoder</string>
+ <string name="sort_title"><b>Sorter på ...</b></string>
+ <string name="sort_title_a_z">Tittel (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Tittel (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Dato (Ny \u2192 Gammel)</string>
+ <string name="sort_date_old_new">Dato (Gammel \u2192 Ny)</string>
+ <string name="sort_duration_short_long">Lengde (Kort \u2192 Lang)</string>
+ <string name="sort_duration_long_short">Lengde (Lang \u2192 Kort)</string>
+</resources>
diff --git a/core/src/main/res/values-no/strings.xml b/core/src/main/res/values-no/strings.xml
deleted file mode 100644
index 645d576a4..000000000
--- a/core/src/main/res/values-no/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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-v16/styles.xml b/core/src/main/res/values-v16/styles.xml
index e7c56b5f5..a92790152 100644
--- a/core/src/main/res/values-v16/styles.xml
+++ b/core/src/main/res/values-v16/styles.xml
@@ -9,7 +9,7 @@
<style name="AntennaPod.Dialog.Title" parent="@android:style/TextAppearance.Medium">
<item name="android:textSize">@dimen/text_size_medium</item>
- <item name="android:textColor">@color/bright_blue</item>
+ <item name="android:textColor">@color/holo_blue_light</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
<item name="android:fontFamily">sans-serif-light</item>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index 566032da8..ff061be4c 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -6,7 +6,8 @@
<color name="grey600">#757575</color>
<color name="light_gray">#bfbfbf</color>
<color name="black">#000000</color>
- <color name="bright_blue">#33B5E5</color>
+ <color name="holo_blue_light">#33B5E5</color>
+ <color name="holo_blue_dark">#0099CC</color>
<color name="ics_gray">#858585</color>
<color name="actionbar_gray">#DDDDDD</color>
<color name="download_success_green">#669900</color>
@@ -26,7 +27,8 @@
<!-- Theme colors -->
<color name="primary_light">#FFFFFF</color>
- <color name="color_accent">#009EC8</color>
+ <color name="highlight_light">#DDDDDD</color>
+ <color name="highlight_dark">#414141</color>
-</resources> \ No newline at end of file
+</resources>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index e0a7a6a7a..151c0b915 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -25,6 +25,7 @@
<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="free_space_label">"%1$s free</string>
<!-- New episodes fragment -->
<string name="recently_published_episodes_label">Recently published</string>
@@ -464,11 +465,15 @@
<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_message">Please choose the base of your data folder. AntennaPod will create the appropriate sub-directories.</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>
<string name="create_folder_error_already_exists">Folder already exists</string>
<string name="create_folder_error">Could not create folder</string>
+ <string name="folder_does_not_exist_error">"%1$s" does not exist</string>
+ <string name="folder_not_readable_error">"%1$s" is not readable</string>
+ <string name="folder_not_writable_error">"%1$s" is not writable</string>
<string name="folder_not_empty_dialog_title">Folder is not empty</string>
<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>
@@ -478,6 +483,7 @@
<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>
<string name="subscribed_label">Subscribed</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 5d1eac12d..b9ee70dbf 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme.AntennaPod.Light" parent="@style/Theme.AppCompat.Light">
+ <style name="Theme.AntennaPod.Light" parent="Theme.AppCompat.Light">
<item name="colorPrimary">@color/primary_light</item>
- <item name="colorAccent">@color/color_accent</item>
+ <item name="colorAccent">@color/holo_blue_light</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
+ <item name="alertDialogTheme">@style/AntennaPod.Dialog.Light</item>
<item name="attr/action_bar_icon_color">@color/grey600</item>
<item name="attr/action_about">@drawable/ic_info_grey600_24dp</item>
<item name="attr/action_search">@drawable/ic_search_grey600_24dp</item>
@@ -49,11 +50,12 @@
<item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
</style>
- <style name="Theme.AntennaPod.Dark" parent="@style/Theme.AppCompat">
- <item name="colorAccent">@color/color_accent</item>
+ <style name="Theme.AntennaPod.Dark" parent="Theme.AppCompat">
+ <item name="colorAccent">@color/holo_blue_dark</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
+ <item name="alertDialogTheme">@style/AntennaPod.Dialog.Dark</item>
<item name="attr/action_bar_icon_color">@color/white</item>
- <item name="attr/action_about">@drawable/ic_info_white_24dp</item>
+ <item name="attr/action_about">@drawable/ic_info_white_24dp</item>g
<item name="attr/action_search">@drawable/ic_search_white_24dp</item>
<item name="attr/action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
<item name="attr/av_download">@drawable/ic_file_download_white_24dp</item>
@@ -96,12 +98,13 @@
<item name="attr/ic_filter">@drawable/ic_filter_white_24dp</item>
</style>
- <style name="Theme.AntennaPod.Light.NoTitle" parent="@style/Theme.AppCompat.Light.NoActionBar">
+ <style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">true</item>
<item name="colorPrimary">@color/primary_light</item>
- <item name="colorAccent">@color/color_accent</item>
+ <item name="colorAccent">@color/holo_blue_light</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
+ <item name="alertDialogTheme">@style/AntennaPod.Dialog.Light</item>
<item name="attr/action_about">@drawable/ic_info_grey600_24dp</item>
<item name="attr/action_search">@drawable/ic_search_grey600_24dp</item>
<item name="attr/action_stream">@drawable/ic_settings_input_antenna_grey600_24dp</item>
@@ -145,11 +148,12 @@
<item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
</style>
- <style name="Theme.AntennaPod.Dark.NoTitle" parent="@style/Theme.AppCompat.NoActionBar">
+ <style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">true</item>
- <item name="colorAccent">@color/color_accent</item>
+ <item name="colorAccent">@color/holo_blue_dark</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
+ <item name="alertDialogTheme">@style/AntennaPod.Dialog.Dark</item>
<item name="attr/action_about">@drawable/ic_info_white_24dp</item>
<item name="attr/action_search">@drawable/ic_search_white_24dp</item>
<item name="attr/action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
@@ -197,44 +201,6 @@
<item name="windowActionBarOverlay">true</item>
</style>
- <style name="UndoBar">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">48dp</item>
- <item name="android:layout_gravity">bottom</item>
- <item name="android:layout_marginLeft">8dp</item>
- <item name="android:layout_marginRight">8dp</item>
- <item name="android:layout_marginBottom">16dp</item>
- <item name="android:orientation">horizontal</item>
- <item name="android:background">@drawable/undobar</item>
- <item name="android:clickable">true</item>
- <item name="android:divider">@drawable/undobar_divider</item>
- </style>
-
- <style name="UndoBarMessage">
- <item name="android:layout_width">0dp</item>
- <item name="android:layout_weight">1</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_marginLeft">16dp</item>
- <item name="android:layout_gravity">center_vertical</item>
- <item name="android:layout_marginRight">16dp</item>
- <item name="android:textAppearance">?android:textAppearanceMedium</item>
- <item name="android:textColor">#fff</item>
- </style>
-
- <style name="UndoBarButton">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">match_parent</item>
- <item name="android:paddingLeft">16dp</item>
- <item name="android:paddingRight">16dp</item>
- <item name="android:background">@drawable/undobar_button</item>
- <item name="android:drawableLeft">@drawable/ic_undobar_undo</item>
- <item name="android:drawablePadding">12dp</item>
- <item name="android:textAppearance">?android:textAppearanceSmall</item>
- <item name="android:textStyle">bold</item>
- <item name="android:textColor">#fff</item>
- <item name="android:text">@string/undo</item>
- </style>
-
<style name="AntennaPod.TextView.Heading" parent="@android:style/TextAppearance.Medium">
<item name="android:textSize">@dimen/text_size_large</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -271,6 +237,14 @@
<item name="textAllCaps">false</item>
</style>
+ <style name="AntennaPod.Dialog.Light" parent="Theme.AppCompat.Light.Dialog">
+ <item name="colorAccent">@color/holo_blue_light</item>
+ </style>
+
+ <style name="AntennaPod.Dialog.Dark" parent="Theme.AppCompat.Dialog">
+ <item name="colorAccent">@color/holo_blue_dark</item>
+ </style>
+
<style name="BigBlurryBackground">
<item name="android:scaleType">centerCrop</item>
<!-- <item name="android:tint">@color/image_readability_tint</item> -->
diff --git a/description/it_IT.txt b/description/it_IT.txt
index 34099b578..59854c7d0 100644
--- a/description/it_IT.txt
+++ b/description/it_IT.txt
@@ -5,37 +5,37 @@ But most importantly: Download, stream or queue episodes and enjoy them the way
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-<b>All features:</b><br>
+<b>Funzioni:</b><br>
IMPORTA, ORGANIZZA E RIPRODUCI<br>
-&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
+&#8226; Aggiungi e importa feed via iTunes, gPodder.net, file OPML e link RSS o Atom<br>
&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
-&#8226; Access password-protected feeds and episodes<br>
+&#8226; Accedi a feed e episodi protetti da password<br>
&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
-KEEP TRACK, SHARE & APPRECIATE<br>
-&#8226; Keep track of the best of the best by marking episodes as favourites<br>
+TIENI TRACCIA, CONDIVIDI & APPREZZA<br>
+&#8226; Tieni traccia del meglio del meglio segnando i tuoi episodi preferiti<br>
&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
&#8226; Support content creators with Flattr integration including automatic flattring
-CONTROL THE SYSTEM<br>
+CONTROLLA IL SISTEMA<br>
&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
&#8226; Adapt to your environment using the light and dark theme<br>
&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
-<b>Join the AntennaPod community!</b><br>
-AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
+<b>Entra nella community di AntennaPod!</b><br>
+AntennaPod è sviluppato da volontari. Anche tu puoi contribuire, con il codice o con dei commenti!
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+GitHub è il posto nel quale fare richieste, segnalare bug e contribuire allo sviluppo:<br>
www.github.com/AntennaPod/AntennaPod
-Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
+Il nostro Gruppo Google è il posto dove condividere le tue idee, podcast preferiti e gratitudine a tutti i nostri volontari:<br>
https://groups.google.com/forum/#!forum/antennapod
-Transifex is the place to help with translations:<br>
+Transifex è il posto dove puoi aiutare a tradurre AntennaPod:<br>
www.transifex.com/antennapod/antennapod
Check out our Beta Testing programme to get the latest features first:<br>
diff --git a/description/ko_KR.txt b/description/ko_KR.txt
new file mode 100644
index 000000000..14e6a83c8
--- /dev/null
+++ b/description/ko_KR.txt
@@ -0,0 +1,42 @@
+Easy-to-use, flexible and open-source podcast manager and player
+
+AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
+But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
+
+Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+
+<b>All features:</b><br>
+IMPORT, ORGANIZE AND PLAY<br>
+&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
+&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
+&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
+&#8226; Access password-protected feeds and episodes<br>
+&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
+
+KEEP TRACK, SHARE & APPRECIATE<br>
+&#8226; Keep track of the best of the best by marking episodes as favourites<br>
+&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
+&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
+&#8226; Support content creators with Flattr integration including automatic flattring
+
+CONTROL THE SYSTEM<br>
+&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
+&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
+&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; Adapt to your environment using the light and dark theme<br>
+&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
+
+<b>Join the AntennaPod community!</b><br>
+AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
+
+GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+www.github.com/AntennaPod/AntennaPod
+
+Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
+https://groups.google.com/forum/#!forum/antennapod
+
+Transifex is the place to help with translations:<br>
+www.transifex.com/antennapod/antennapod
+
+Check out our Beta Testing programme to get the latest features first:<br>
+www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/description/uk_UA.txt b/description/uk_UA.txt
index daeb872fe..a123f3854 100644
--- a/description/uk_UA.txt
+++ b/description/uk_UA.txt
@@ -1,42 +1,42 @@
Легкий у використанні та гнучкий плейер та менеджер подкастів з відкритим сирцевим кодом
-AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
-But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
-
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-
-<b>All features:</b><br>
-IMPORT, ORGANIZE AND PLAY<br>
-&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
-&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
-&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
-&#8226; Access password-protected feeds and episodes<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
-
-KEEP TRACK, SHARE & APPRECIATE<br>
-&#8226; Keep track of the best of the best by marking episodes as favourites<br>
-&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
-&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
-&#8226; Support content creators with Flattr integration including automatic flattring
-
-CONTROL THE SYSTEM<br>
-&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
-&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
-&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adapt to your environment using the light and dark theme<br>
-&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
-
-<b>Join the AntennaPod community!</b><br>
-AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
-
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+AntennaPod це менеджер подкастів та плейер що дає доступ до мільйонів безкоштовних та комерційних подкастів від незалежних подкастерів та великих корпорацій таких як BBC, NPR та CNN. Є можливість додавати, імпортувати та експортувати канали за допомогою бази даних подкастів iTunes, файлів OPML або простих посилань на RSS. Зберігайте зусилля, живлення та мобільний трафік за допомогою автоматичного завантаження епізодів (зазначивши час, інтервали та мережі WiFi) та видалення епізодів (з урахуванням ваших побажань та налаштувань).<br>
+Але найважливіше: завантажуйте, слухайте або додавайте до черги епізоди так, як вам подобається з налаштуванням швидкості програвання, підтримкою розділів та таймера сну. Можливо навіть фінансово заохочувати авторів за допомогою інтергації з сервісом Flattr.
+
+Зроблений ентузіастом подкастів, AntennaPod є вільним в усіх сенсах цього слова: відкриті вихідні тексти, безкоштовний, без реклами.
+
+<b>Всі можливості:</b><br>
+ІМПОРТУЙТЕ, ВПОРЯДКОВУЙТЕ ТА СЛУХАЙТЕ<br>
+&#8226; Додавайте та імпортуйте канали з каталогів iTunes та gPodder.net, файлів OPML та з посилань на RSS або Atom<br>
+&#8226; Керуйте програванням будь-де: з віджета, нотифікації, кнопками навушників або через блютус<br>
+&#8226; Слухайте так як вам подобається з налаштуванням швидкості програвання, підтримкой розділів (в форматах MP3, VorbisComment та Podlove), зберіганням момента програвання та таймером сну (перезапуск струсом, зниження гучності та уповільнення програвання)<br>
+&#8226; Доступ до каналів та епізодів що захищені паролем<br>
+&#8226; Використовуйте посторінкові канали (www.podlove.org/paged-feeds)
+
+ВІДСТЕЖУЙТЕ, ДІЛІТЬСЯ ТА ОЦІНЮЙТЕ<br>
+&#8226; Зберігайте найкраще в улюблених епізодах<br>
+&#8226; Відшукайте той самий епізод в історії програвання або пошуком (в назвах і нотатках)<br>
+&#8226; Діліться епізодами та каналами за допомогою соцмереж та пошти, сервісів gPodder.net та через експорт OPML файлів<br>
+&#8226; Підтримуйте авторів за допомогою інтегрованого сервіса Flattr з можливістю автоматичной підтримки
+
+КЕРУЙТЕ СИСТЕМОЙ<br>
+&#8226; Керуйте автоматичним завантаженням: вибирайте канали, мобільні мережі, мережі WiFi, завантажуйте тільки під час зарядки або у встановлений час і інтервали<br>
+&#8226; Керуйте збереженням, встановлюйте ліміт на кеш епізодів, налагоджуйте розумне видалення (з урахуванням улюблених епізодів і статуса програвання) та вибирайте місце зберігання<br>
+&#8226; Користуйтесь AntennaPod вашою мовою (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; Пристосовуйтесь до ваших умов, користуйтесь світлой або темной темами<br>
+&#8226; Зберігайте ваші підписки на gPodder.net або експортуйте в файл OPML
+
+<b>Долучайтесь до спільноти AntennaPod!</b><br>
+AntennaPod швидко розвивається волонтерами. Ви також маєте змогу допомогти, кодом або зауваженнями!
+
+Долучитись до проекта, повідомити про ваші побажання та про помилки можна на GitHub:<br>
www.github.com/AntennaPod/AntennaPod
-Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
+В нашій групі на Google можна поділитись ідеями, улюбленими моментами з подкастінга та добрими побажаннями волонтерам:<br>
https://groups.google.com/forum/#!forum/antennapod
-Transifex is the place to help with translations:<br>
+Допомагайте з перекладом на Transifex:<br>
www.transifex.com/antennapod/antennapod
-Check out our Beta Testing programme to get the latest features first:<br>
+Зверніть увагу на нашу програму для бета тестування якщо бажаєте получати найновіші версії в першу чергу:<br>
www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/gradlew.bat b/gradlew.bat
index aec99730b..8a0b282aa 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,90 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega