diff options
46 files changed, 913 insertions, 390 deletions
diff --git a/app/build.gradle b/app/build.gradle index f83b405f7..d15427aef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -160,6 +160,8 @@ dependencies { compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion" compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion" + + compile 'com.github.mfietz:fyydlin:v0.1' } play { 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 15313d772..1e03f99fa 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -10,6 +10,7 @@ import android.database.DataSetObserver; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; @@ -38,6 +39,7 @@ import de.danoeh.antennapod.R; 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.MessageEvent; import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.feed.EventDistributor; @@ -733,6 +735,18 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } + public void onEventMainThread(MessageEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + View parentLayout = findViewById(R.id.drawer_layout); + Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); + if(event.action != null) { + snackbar.setAction(getString(R.string.undo), v -> { + event.action.run(); + }); + } + snackbar.show(); + } + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override 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 48b578be7..1cb936296 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -47,6 +47,7 @@ import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.dialog.SleepTimerDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog; +import de.greenrobot.event.EventBus; import rx.Observable; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; @@ -217,6 +218,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.pause(); } super.onPause(); + EventBus.getDefault().unregister(this); } /** @@ -580,6 +582,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements if(controller != null) { controller.init(); } + EventBus.getDefault().register(this); } /** diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java index d9b2d10da..4a24f0329 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -8,6 +8,7 @@ import android.content.res.Configuration; import android.os.Build; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; @@ -36,6 +37,7 @@ import de.danoeh.antennapod.adapter.ChaptersListAdapter; 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.MessageEvent; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; @@ -464,7 +466,17 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - + public void onEventMainThread(MessageEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + View parentLayout = findViewById(R.id.drawer_layout); + Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); + if (event.action != null) { + snackbar.setAction(getString(R.string.undo), v -> { + event.action.run(); + }); + } + snackbar.show(); + } private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java index bc1a40b11..355e0f372 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java @@ -15,7 +15,7 @@ import java.util.ArrayList; import java.util.List; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.opml.OpmlElement; +import de.danoeh.antennapod.core.export.opml.OpmlElement; import de.danoeh.antennapod.core.preferences.UserPreferences; /** @@ -23,10 +23,8 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; * which feeds he wants to import. */ public class OpmlFeedChooserActivity extends AppCompatActivity { - private static final String TAG = "OpmlFeedChooserActivity"; - public static final String EXTRA_SELECTED_ITEMS = "de.danoeh.antennapod.selectedItems"; - + private static final String TAG = "OpmlFeedChooserActivity"; private Button butConfirm; private Button butCancel; private ListView feedlist; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java index 8726af281..8b87aaaaf 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import de.danoeh.antennapod.R; import de.danoeh.antennapod.asynctask.OpmlFeedQueuer; import de.danoeh.antennapod.asynctask.OpmlImportWorker; -import de.danoeh.antennapod.core.opml.OpmlElement; +import de.danoeh.antennapod.core.export.opml.OpmlElement; import de.danoeh.antennapod.core.util.LangUtils; /** @@ -29,9 +29,8 @@ import de.danoeh.antennapod.core.util.LangUtils; public class OpmlImportBaseActivity extends AppCompatActivity { private static final String TAG = "OpmlImportBaseActivity"; - private OpmlImportWorker importWorker; - private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5; + private OpmlImportWorker importWorker; @Nullable private Uri uri; /** diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java index 7afa270cc..b01cf43e4 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java @@ -1,9 +1,9 @@ package de.danoeh.antennapod.activity; -import de.danoeh.antennapod.core.opml.OpmlElement; - import java.util.ArrayList; +import de.danoeh.antennapod.core.export.opml.OpmlElement; + /** * Hold infos gathered by Ompl-Import * <p/> diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java index 61765d6b7..dd932814f 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -26,11 +26,9 @@ import de.danoeh.antennapod.preferences.PreferenceController; */ public class PreferenceActivity extends AppCompatActivity { + private static WeakReference<PreferenceActivity> instance; private PreferenceController preferenceController; private MainFragment prefFragment; - private static WeakReference<PreferenceActivity> instance; - - private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() { @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override @@ -128,5 +126,14 @@ public class PreferenceActivity extends AppCompatActivity { } super.onPause(); } + + @Override + public void onStop() { + PreferenceActivity activity = instance.get(); + if(activity != null && activity.preferenceController != null) { + activity.preferenceController.onStop(); + } + super.onStop(); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java index 4af988ea0..390bec15c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java @@ -18,9 +18,6 @@ import de.danoeh.antennapod.preferences.PreferenceController; */ public class PreferenceActivityGingerbread extends android.preference.PreferenceActivity { private static final String TAG = "PreferenceActivity"; - - private PreferenceController preferenceController; - private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() { @SuppressWarnings("deprecation") @@ -34,6 +31,7 @@ public class PreferenceActivityGingerbread extends android.preference.Preference return PreferenceActivityGingerbread.this; } }; + private PreferenceController preferenceController; @SuppressLint("NewApi") @SuppressWarnings("deprecation") @@ -61,6 +59,12 @@ public class PreferenceActivityGingerbread extends android.preference.Preference } @Override + protected void onStop() { + preferenceController.onStop(); + super.onStop(); + } + + @Override protected void onApplyThemeResource(Theme theme, int resid, boolean first) { theme.applyStyle(UserPreferences.getTheme(), true); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java index e9756b467..e381b4651 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.adapter.itunes; import android.content.Context; +import android.support.annotation.NonNull; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -18,6 +19,7 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.mfietz.fyydlin.SearchHit; public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { /** @@ -42,8 +44,9 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { this.context = context; } + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { //Current podcast Podcast podcast = data.get(position); @@ -87,35 +90,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { } /** - * View holder object for the GridView - */ - class PodcastViewHolder { - - /** - * ImageView holding the Podcast image - */ - public final ImageView coverView; - - /** - * TextView holding the Podcast title - */ - public final TextView titleView; - - public final TextView urlView; - - - /** - * Constructor - * @param view GridView cell - */ - PodcastViewHolder(View view){ - coverView = (ImageView) view.findViewById(R.id.imgvCover); - titleView = (TextView) view.findViewById(R.id.txtvTitle); - urlView = (TextView) view.findViewById(R.id.txtvUrl); - } - } - - /** * Represents an individual podcast on the iTunes Store. */ public static class Podcast { //TODO: Move this out eventually. Possibly to core.itunes.model @@ -154,6 +128,10 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { return new Podcast(title, imageUrl, feedUrl); } + public static Podcast fromSearch(SearchHit searchHit) { + return new Podcast(searchHit.getTitle(), searchHit.getImageUrl(), searchHit.getXmlUrl()); + } + /** * Constructs a Podcast instance from iTunes toplist entry * @@ -177,4 +155,33 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { } } + + /** + * View holder object for the GridView + */ + class PodcastViewHolder { + + /** + * ImageView holding the Podcast image + */ + final ImageView coverView; + + /** + * TextView holding the Podcast title + */ + final TextView titleView; + + final TextView urlView; + + + /** + * Constructor + * @param view GridView cell + */ + PodcastViewHolder(View view){ + coverView = (ImageView) view.findViewById(R.id.imgvCover); + titleView = (TextView) view.findViewById(R.id.txtvTitle); + urlView = (TextView) view.findViewById(R.id.txtvUrl); + } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java new file mode 100644 index 000000000..192df8ca5 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java @@ -0,0 +1,65 @@ +package de.danoeh.antennapod.asynctask; + +import android.support.annotation.NonNull; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import de.danoeh.antennapod.core.export.ExportWriter; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.LangUtils; +import rx.Observable; + +/** + * Writes an OPML file into the export directory in the background. + */ +public class ExportWorker { + + private static final String EXPORT_DIR = "export/"; + private static final String TAG = "ExportWorker"; + private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds"; + + private ExportWriter exportWriter; + private File output; + + public ExportWorker(ExportWriter exportWriter) { + this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR), + DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension())); + } + + public ExportWorker(ExportWriter exportWriter, @NonNull File output) { + this.exportWriter = exportWriter; + this.output = output; + } + + public Observable<File> exportObservable() { + if (output.exists()) { + Log.w(TAG, "Overwriting previously exported file."); + output.delete(); + } + return Observable.create(subscriber -> { + OutputStreamWriter writer = null; + try { + writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8); + exportWriter.writeDocument(DBReader.getFeedList(), writer); + subscriber.onNext(output); + } catch (IOException e) { + subscriber.onError(e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + subscriber.onError(e); + } + } + subscriber.onCompleted(); + } + }); + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java deleted file mode 100644 index 13abb26ea..000000000 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java +++ /dev/null @@ -1,122 +0,0 @@ -package de.danoeh.antennapod.asynctask; - -import android.annotation.SuppressLint; -import android.app.ProgressDialog; -import android.content.Context; -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; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; - -import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.opml.OpmlWriter; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.util.LangUtils; - -/** - * Writes an OPML file into the export directory in the background. - */ -public class OpmlExportWorker extends AsyncTask<Void, Void, Void> { - private static final String TAG = "OpmlExportWorker"; - private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds.opml"; - public static final String EXPORT_DIR = "export/"; - - private Context context; - private File output; - - private ProgressDialog progDialog; - private Exception exception; - - public OpmlExportWorker(Context context, File output) { - this.context = context; - this.output = output; - } - - public OpmlExportWorker(Context context) { - this.context = context; - } - - @Override - protected Void doInBackground(Void... params) { - OpmlWriter opmlWriter = new OpmlWriter(); - if (output == null) { - output = new File( - UserPreferences.getDataFolder(EXPORT_DIR), - DEFAULT_OUTPUT_NAME); - if (output.exists()) { - Log.w(TAG, "Overwriting previously exported file."); - output.delete(); - } - } - OutputStreamWriter writer = null; - try { - writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8); - opmlWriter.writeDocument(DBReader.getFeedList(), writer); - } catch (IOException e) { - e.printStackTrace(); - exception = e; - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ioe) { - exception = ioe; - } - } - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - progDialog.dismiss(); - AlertDialog.Builder alert = new AlertDialog.Builder(context) - .setNeutralButton(android.R.string.ok, - (dialog, which) -> dialog.dismiss()); - if (exception != null) { - alert.setTitle(R.string.export_error_label); - alert.setMessage(exception.getMessage()); - } else { - alert.setTitle(R.string.opml_export_success_title); - alert.setMessage(context - .getString(R.string.opml_export_success_sum) - + output.toString()) - .setPositiveButton(R.string.send_label, (dialog, which) -> { - Uri outputUri = Uri.fromFile(output); - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_SUBJECT, - context.getResources().getText(R.string.opml_export_label)); - sendIntent.putExtra(Intent.EXTRA_STREAM, outputUri); - sendIntent.setType("text/plain"); - context.startActivity(Intent.createChooser(sendIntent, - context.getResources().getText(R.string.send_label))); - }); - } - alert.create().show(); - } - - @Override - protected void onPreExecute() { - progDialog = new ProgressDialog(context); - progDialog.setMessage(context.getString(R.string.exporting_label)); - progDialog.setIndeterminate(true); - progDialog.show(); - } - - @SuppressLint("NewApi") - public void executeAsync() { - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - executeOnExecutor(THREAD_POOL_EXECUTOR); - } else { - execute(); - } - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java index 1cb653f01..4449d82c2 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java @@ -9,8 +9,8 @@ import java.util.Arrays; import de.danoeh.antennapod.activity.OpmlImportHolder; import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.export.opml.OpmlElement; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.opml.OpmlElement; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; 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 f3b3aeca9..62ea85811 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java @@ -14,8 +14,8 @@ import java.io.Reader; import java.util.ArrayList; import de.danoeh.antennapod.core.R; -import de.danoeh.antennapod.core.opml.OpmlElement; -import de.danoeh.antennapod.core.opml.OpmlReader; +import de.danoeh.antennapod.core.export.opml.OpmlElement; +import de.danoeh.antennapod.core.export.opml.OpmlReader; public class OpmlImportWorker extends AsyncTask<Void, Void, ArrayList<OpmlElement>> { 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 8a13a75d9..0ddee9f61 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import android.content.SharedPreferences; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -16,36 +15,25 @@ import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; -import java.util.concurrent.TimeUnit; - import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; public abstract class SleepTimerDialog { private static final String TAG = SleepTimerDialog.class.getSimpleName(); - private static final int DEFAULT_SPINNER_POSITION = 1; - private Context context; - private String PREF_NAME = "SleepTimerDialog"; - private String PREF_VALUE = "LastValue"; - private String PREF_TIME_UNIT = "LastTimeUnit"; - private String PREF_VIBRATE = "Vibrate"; - 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 CheckBox chAutoEnable; - private TimeUnit[] units = { TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS }; - public SleepTimerDialog(Context context) { this.context = context; - prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); } public MaterialDialog createNewDialog() { @@ -58,7 +46,7 @@ public abstract class SleepTimerDialog { builder.onPositive((dialog, which) -> { try { savePreferences(); - long input = readTimeMillis(); + long input = SleepTimerPreferences.timerMillis(); onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked()); dialog.dismiss(); } catch (NumberFormatException e) { @@ -75,8 +63,9 @@ public abstract class SleepTimerDialog { spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit); cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset); cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate); + chAutoEnable = (CheckBox) view.findViewById(R.id.chAutoEnable); - etxtTime.setText(prefs.getString(PREF_VALUE, "15")); + etxtTime.setText(SleepTimerPreferences.lastTimerValue()); etxtTime.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable s) { @@ -104,11 +93,11 @@ public abstract class SleepTimerDialog { android.R.layout.simple_spinner_item, spinnerContent); spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spTimeUnit.setAdapter(spinnerAdapter); - int selection = prefs.getInt(PREF_TIME_UNIT, DEFAULT_SPINNER_POSITION); - spTimeUnit.setSelection(selection); + spTimeUnit.setSelection(SleepTimerPreferences.lastTimerTimeUnit()); - cbShakeToReset.setChecked(prefs.getBoolean(PREF_SHAKE_TO_RESET, true)); - cbVibrate.setChecked(prefs.getBoolean(PREF_VIBRATE, true)); + cbShakeToReset.setChecked(SleepTimerPreferences.shakeToReset()); + cbVibrate.setChecked(SleepTimerPreferences.vibrate()); + chAutoEnable.setChecked(SleepTimerPreferences.autoEnable()); return dialog; } @@ -125,19 +114,12 @@ public abstract class SleepTimerDialog { public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate); - private long readTimeMillis() { - TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()]; - long value = Long.parseLong(etxtTime.getText().toString()); - return selectedUnit.toMillis(value); - } - private void savePreferences() { - prefs.edit() - .putString(PREF_VALUE, etxtTime.getText().toString()) - .putInt(PREF_TIME_UNIT, spTimeUnit.getSelectedItemPosition()) - .putBoolean(PREF_SHAKE_TO_RESET, cbShakeToReset.isChecked()) - .putBoolean(PREF_VIBRATE, cbVibrate.isChecked()) - .apply(); + SleepTimerPreferences.setLastTimer(etxtTime.getText().toString(), + spTimeUnit.getSelectedItemPosition()); + SleepTimerPreferences.setShakeToReset(cbShakeToReset.isChecked()); + SleepTimerPreferences.setVibrate(cbVibrate.isChecked()); + SleepTimerPreferences.setAutoEnable(chAutoEnable.isChecked()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index 45364ca07..f14ebbdaf 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -39,10 +39,11 @@ public class AddFeedFragment extends Fragment { etxtFeedurl.setText(args.getString(ARG_FEED_URL)); } + Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes); Button butBrowserGpoddernet = (Button) root.findViewById(R.id.butBrowseGpoddernet); + Button butSearchFyyd = (Button) root.findViewById(R.id.butSearchFyyd); Button butOpmlImport = (Button) root.findViewById(R.id.butOpmlImport); Button butConfirm = (Button) root.findViewById(R.id.butConfirm); - Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes); final MainActivity activity = (MainActivity) getActivity(); activity.getSupportActionBar().setTitle(R.string.add_feed_label); @@ -51,6 +52,8 @@ public class AddFeedFragment extends Fragment { butBrowserGpoddernet.setOnClickListener(v -> activity.loadChildFragment(new GpodnetMainFragment())); + butSearchFyyd.setOnClickListener(v -> activity.loadChildFragment(new FyydSearchFragment())); + butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(), OpmlImportFromPathActivity.class))); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java new file mode 100644 index 000000000..7fcf9c93d --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java @@ -0,0 +1,191 @@ +package de.danoeh.antennapod.fragment; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.SearchView; +import android.util.Log; +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.Button; +import android.widget.GridView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.OnlineFeedViewActivity; +import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.mfietz.fyydlin.FyydClient; +import de.mfietz.fyydlin.FyydResponse; +import de.mfietz.fyydlin.SearchHit; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast; +import static java.util.Collections.emptyList; + +public class FyydSearchFragment extends Fragment { + + private static final String TAG = "FyydSearchFragment"; + + /** + * Adapter responsible with the search results + */ + private ItunesAdapter adapter; + private GridView gridView; + private ProgressBar progressBar; + private TextView txtvError; + private Button butRetry; + private TextView txtvEmpty; + + private FyydClient client = new FyydClient(); + + /** + * List of podcasts retreived from the search + */ + private List<Podcast> searchResults; + private Subscription subscription; + + /** + * Constructor + */ + public FyydSearchFragment() { + // Required empty public constructor + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); + gridView = (GridView) root.findViewById(R.id.gridView); + adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); + gridView.setAdapter(adapter); + + //Show information about the podcast when the list item is clicked + gridView.setOnItemClickListener((parent, view1, position, id) -> { + Podcast podcast = searchResults.get(position); + Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); + intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); + intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title); + startActivity(intent); + }); + progressBar = (ProgressBar) root.findViewById(R.id.progressBar); + txtvError = (TextView) root.findViewById(R.id.txtvError); + butRetry = (Button) root.findViewById(R.id.butRetry); + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + + return root; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (subscription != null) { + subscription.unsubscribe(); + } + adapter = null; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.itunes_search, menu); + MenuItem searchItem = menu.findItem(R.id.action_search); + final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); + MenuItemUtils.adjustTextColor(getActivity(), sv); + sv.setQueryHint(getString(R.string.search_fyyd_label)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + search(s); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + getActivity().getSupportFragmentManager().popBackStack(); + return true; + } + }); + MenuItemCompat.expandActionView(searchItem); + } + + private void search(String query) { + if (subscription != null) { + subscription.unsubscribe(); + } + showOnlyProgressBar(); + subscription = client.searchPodcasts(query) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + progressBar.setVisibility(View.GONE); + processSearchResult(result); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.toString()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> search(query)); + butRetry.setVisibility(View.VISIBLE); + }); + } + + private void showOnlyProgressBar() { + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.GONE); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progressBar.setVisibility(View.VISIBLE); + } + + void processSearchResult(FyydResponse response) { + adapter.clear(); + if (!response.getData().isEmpty()) { + adapter.clear(); + searchResults = new ArrayList<>(); + for (SearchHit searchHit : response.getData().values()) { + Podcast podcast = Podcast.fromSearch(searchHit); + searchResults.add(podcast); + } + } else { + searchResults = emptyList(); + } + for(Podcast podcast : searchResults) { + adapter.add(podcast); + } + adapter.notifyDataSetInvalidated(); + gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE); + txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE); + } + +} 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 85eaafcad..818b3a625 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.preferences; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.ProgressDialog; import android.app.TimePickerDialog; import android.content.ActivityNotFoundException; import android.content.Context; @@ -54,7 +55,10 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.activity.PreferenceActivityGingerbread; import de.danoeh.antennapod.activity.StatisticsActivity; -import de.danoeh.antennapod.asynctask.OpmlExportWorker; +import de.danoeh.antennapod.asynctask.ExportWorker; +import de.danoeh.antennapod.core.export.ExportWriter; +import de.danoeh.antennapod.core.export.html.HtmlWriter; +import de.danoeh.antennapod.core.export.opml.OpmlWriter; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.GpodnetSyncService; @@ -66,6 +70,10 @@ import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog; import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog; import de.danoeh.antennapod.dialog.ProxyDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Sets up a preference UI that lets the user change user preferences. @@ -80,6 +88,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess"; private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs"; private static final String PREF_OPML_EXPORT = "prefOpmlExport"; + private static final String PREF_HTML_EXPORT = "prefHtmlExport"; private static final String STATISTICS = "statistics"; private static final String PREF_ABOUT = "prefAbout"; private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir"; @@ -97,32 +106,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc private static final String PREF_KNOWN_ISSUES = "prefKnownIssues"; private static final String PREF_FAQ = "prefFaq"; private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport"; - - private final PreferenceUI ui; - - private CheckBoxPreference[] selectedNetworks; - private static final String[] EXTERNAL_STORAGE_PERMISSIONS = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41; - - public PreferenceController(PreferenceUI ui) { - this.ui = ui; - PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext()) - .registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if(key.equals(UserPreferences.PREF_SONIC)) { - CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC); - if(prefSonic != null) { - prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false)); - } - } - } - + private final PreferenceUI ui; private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener = (sharedPreferences, key) -> { if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) { @@ -130,6 +118,14 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc GpodnetPreferences.getLastSyncAttemptTimestamp()); } }; + private CheckBoxPreference[] selectedNetworks; + private Subscription subscription; + + public PreferenceController(PreferenceUI ui) { + this.ui = ui; + PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext()) + .registerOnSharedPreferenceChangeListener(this); + } /** * Returns the preference activity that should be used on this device. @@ -144,6 +140,16 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if(key.equals(UserPreferences.PREF_SONIC)) { + CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC); + if(prefSonic != null) { + prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false)); + } + } + } + public void onCreate() { final Activity activity = ui.getActivity(); @@ -179,11 +185,9 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } ); ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener( - preference -> { - new OpmlExportWorker(activity).executeAsync(); - return true; - } - ); + preference -> export(new OpmlWriter())); + ui.findPreference(PreferenceController.PREF_HTML_EXPORT).setOnPreferenceClickListener( + preference -> export(new HtmlWriter())); ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener( preference -> { if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT && @@ -440,6 +444,40 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter()); } + private boolean export(ExportWriter exportWriter) { + Context context = ui.getActivity(); + final ProgressDialog progressDialog = new ProgressDialog(context); + progressDialog.setMessage(context.getString(R.string.exporting_label)); + progressDialog.setIndeterminate(true); + progressDialog.show(); + final AlertDialog.Builder alert = new AlertDialog.Builder(context) + .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); + Observable<File> observable = new ExportWorker(exportWriter).exportObservable(); + subscription = observable.subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(output -> { + alert.setTitle(R.string.opml_export_success_title); + String message = context.getString(R.string.opml_export_success_sum) + output.toString(); + alert.setMessage(message); + alert.setPositiveButton(R.string.send_label, (dialog, which) -> { + Uri outputUri = Uri.fromFile(output); + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_SUBJECT, + context.getResources().getText(R.string.opml_export_label)); + sendIntent.putExtra(Intent.EXTRA_STREAM, outputUri); + sendIntent.setType("text/plain"); + context.startActivity(Intent.createChooser(sendIntent, + context.getResources().getText(R.string.send_label))); + }); + alert.create().show(); + }, error -> { + alert.setTitle(R.string.export_error_label); + alert.setMessage(error.getMessage()); + alert.show(); + }, () -> progressDialog.dismiss()); + return true; + } + private void openInBrowser(String url) { try { Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); @@ -464,6 +502,12 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener); } + public void onStop() { + if(subscription != null) { + subscription.unsubscribe(); + } + } + @SuppressLint("NewApi") public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK && diff --git a/app/src/main/res/layout-v14/time_dialog.xml b/app/src/main/res/layout-v14/time_dialog.xml index 06c2cce14..ba4249268 100644 --- a/app/src/main/res/layout-v14/time_dialog.xml +++ b/app/src/main/res/layout-v14/time_dialog.xml @@ -33,26 +33,34 @@ </LinearLayout> <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:text="@string/timer_about_to_expire_label" + android:textSize="16sp" /> + + <CheckBox + android:id="@+id/cbShakeToReset" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/shake_to_reset_label" /> + + <CheckBox + android:id="@+id/cbVibrate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timer_vibration_label" /> + + <CheckBox + android:id="@+id/chAutoEnable" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="vertical"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:textSize="16sp" - android:text="@string/timer_about_to_expire_label"/> - - <CheckBox android:id="@+id/cbShakeToReset" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/shake_to_reset_label"/> - - <CheckBox android:id="@+id/cbVibrate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/timer_vibration_label"/> + android:text="@string/auto_enable_label" /> </LinearLayout> diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml index dff24c650..33951e060 100644 --- a/app/src/main/res/layout/addfeed.xml +++ b/app/src/main/res/layout/addfeed.xml @@ -7,18 +7,18 @@ <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingTop="8dp" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="8dp" android:paddingLeft="16dp" android:paddingRight="16dp" - android:paddingBottom="8dp" - android:orientation="vertical"> + android:paddingTop="8dp"> <TextView android:id="@+id/txtvPodcastDirectories" + style="@style/AntennaPod.TextView.Heading" android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/AntennaPod.TextView.Heading" android:text="@string/podcastdirectories_label"/> <TextView @@ -26,83 +26,73 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/podcastdirectories_descr" - android:textSize="@dimen/text_size_medium" - android:layout_marginTop="4dp"/> + android:textSize="@dimen/text_size_medium"/> <Button android:id="@+id/butSearchItunes" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" + android:layout_marginTop="4dp" android:text="@string/search_itunes_label"/> <Button + android:id="@+id/butSearchFyyd" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/search_fyyd_label"/> + + <Button android:id="@+id/butBrowseGpoddernet" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" android:text="@string/browse_gpoddernet_label"/> - <View - android:id="@+id/divider1" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_margin="16dp" - android:background="?android:attr/listDivider"/> + <View style="@style/Divider"/> <TextView android:id="@+id/txtvFeedurl" + style="@style/AntennaPod.TextView.Heading" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/divider1" - style="@style/AntennaPod.TextView.Heading" android:text="@string/txtvfeedurl_label"/> <EditText android:id="@+id/etxtFeedurl" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:hint="@string/etxtFeedurlHint" - android:inputType="textUri" + android:cursorVisible="true" android:focusable="true" android:focusableInTouchMode="true" - android:cursorVisible="true"/> + android:hint="@string/etxtFeedurlHint" + android:inputType="textUri"/> <Button android:id="@+id/butConfirm" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" android:text="@string/confirm_label"/> - <View - android:id="@+id/divider2" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_margin="16dp" - android:background="?android:attr/listDivider"/> + <View style="@style/Divider"/> <TextView android:id="@+id/txtvOpmlImport" + style="@style/AntennaPod.TextView.Heading" android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/AntennaPod.TextView.Heading" android:text="@string/opml_import_label"/> <TextView android:id="@+id/txtvOpmlImportExpl" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:textSize="@dimen/text_size_medium" - android:text="@string/opml_import_txtv_button_lable"/> + android:text="@string/opml_import_txtv_button_lable" + android:textSize="@dimen/text_size_medium"/> <Button android:id="@+id/butOpmlImport" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" + android:layout_marginTop="4dp" android:text="@string/opml_import_label"/> </LinearLayout> diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml index b270e82f7..0290ce708 100644 --- a/app/src/main/res/layout/time_dialog.xml +++ b/app/src/main/res/layout/time_dialog.xml @@ -36,22 +36,30 @@ android:layout_height="wrap_content" android:orientation="vertical"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:textSize="16sp" - android:text="@string/timer_about_to_expire_label"/> - - <CheckBox android:id="@+id/cbShakeToReset" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/shake_to_reset_label"/> - - <CheckBox android:id="@+id/cbVibrate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/timer_vibration_label"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:text="@string/timer_about_to_expire_label" + android:textSize="16sp" /> + + <CheckBox + android:id="@+id/cbShakeToReset" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/shake_to_reset_label" /> + + <CheckBox + android:id="@+id/cbVibrate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timer_vibration_label" /> + + <CheckBox + android:id="@+id/chAutoEnable" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/auto_enable_label" /> </LinearLayout> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5f56b14df..fab96338e 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -289,6 +289,9 @@ android:key="prefOpmlExport" android:title="@string/opml_export_label"/> <Preference + android:key="prefHtmlExport" + android:title="@string/html_export_label"/> + <Preference android:key="statistics" android:title="@string/statistics_label"/> </PreferenceCategory> diff --git a/build.gradle b/build.gradle index 221f5a68c..ad88bb7bf 100644 --- a/build.gradle +++ b/build.gradle @@ -6,10 +6,10 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' - classpath "me.tatarka:gradle-retrolambda:3.3.0" + classpath "com.android.tools.build:gradle:2.2.2" + classpath "me.tatarka:gradle-retrolambda:3.3.1" classpath "me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2" - classpath 'com.github.triplet.gradle:play-publisher:1.1.4' + classpath "com.github.triplet.gradle:play-publisher:1.1.4" // Exclude the version that the android plugin depends on. configurations.classpath.exclude group: "com.android.tools.external.lombok" } diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java index eb2e6fc9e..88ae6d6bc 100644 --- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core; import android.content.Context; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.util.NetworkUtils; @@ -43,7 +44,7 @@ public class ClientConfig { UpdateManager.init(context); PlaybackPreferences.init(context); NetworkUtils.init(context); -// CastManager.init(context); + SleepTimerPreferences.init(context); initialized = true; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java index e475e696c..67c460e78 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java +++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java @@ -38,10 +38,11 @@ public class FeedRemover extends AsyncTask<Void, Void, Void> { @Override protected void onPostExecute(Void result) { - dialog.dismiss(); + if(dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } if(skipOnCompletion) { - context.sendBroadcast(new Intent( - PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); + context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java index 982015314..80ce6cf56 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java +++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java @@ -8,6 +8,7 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; +import org.apache.commons.io.IOUtils; import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayOutputStream; @@ -27,10 +28,10 @@ import java.util.ArrayList; import java.util.Arrays; import de.danoeh.antennapod.core.BuildConfig; +import de.danoeh.antennapod.core.export.opml.OpmlElement; +import de.danoeh.antennapod.core.export.opml.OpmlReader; +import de.danoeh.antennapod.core.export.opml.OpmlWriter; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.opml.OpmlElement; -import de.danoeh.antennapod.core.opml.OpmlReader; -import de.danoeh.antennapod.core.opml.OpmlWriter; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -56,7 +57,9 @@ public class OpmlBackupAgent extends BackupAgentHelper { } } - /** Class for backing up and restoring the OPML file. */ + /** + * Class for backing up and restoring the OPML file. + */ private static class OpmlBackupHelper implements BackupHelper { private static final String TAG = "OpmlBackupHelper"; @@ -64,7 +67,9 @@ public class OpmlBackupAgent extends BackupAgentHelper { private final Context mContext; - /** Checksum of restored OPML file */ + /** + * Checksum of restored OPML file + */ private byte[] mChecksum; public OpmlBackupHelper(Context context) { @@ -170,12 +175,7 @@ public class OpmlBackupAgent extends BackupAgentHelper { } catch (IOException e) { Log.e(TAG, "Failed to restore OPML backup", e); } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - } - } + IOUtils.closeQuietly(reader); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java new file mode 100644 index 000000000..9fc488fbc --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/event/MessageEvent.java @@ -0,0 +1,21 @@ +package de.danoeh.antennapod.core.event; + +import android.support.annotation.Nullable; + +public class MessageEvent { + + public final String message; + + @Nullable + public final Runnable action; + + public MessageEvent(String message) { + this(message, null); + } + + public MessageEvent(String message, Runnable action) { + this.message = message; + this.action = action; + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java new file mode 100644 index 000000000..3ed251047 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/export/CommonSymbols.java @@ -0,0 +1,24 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.danoeh.antennapod.core.export; + +public class CommonSymbols { + + public static final String HEAD = "head"; + public static final String BODY = "body"; + public static final String TITLE = "title"; + + public static final String XML_FEATURE_INDENT_OUTPUT = "http://xmlpull.org/v1/doc/features.html#indent-output"; + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java new file mode 100644 index 000000000..d6a187b21 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.danoeh.antennapod.core.export; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import de.danoeh.antennapod.core.feed.Feed; + +public interface ExportWriter { + + void writeDocument(List<Feed> feeds, Writer writer) + throws IllegalArgumentException, IllegalStateException, IOException; + + String fileExtension(); + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java new file mode 100644 index 000000000..b8807a686 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.danoeh.antennapod.core.export.html; + +import de.danoeh.antennapod.core.export.CommonSymbols; + +class HtmlSymbols extends CommonSymbols { + + static final String HTML = "html"; + + static final String ORDERED_LIST = "ol"; + static final String LIST_ITEM = "li"; + + static String HEADING = "h1"; + + static final String LINK = "a"; + static final String LINK_DESTINATION = "href"; + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java new file mode 100644 index 000000000..c24b39812 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java @@ -0,0 +1,82 @@ +package de.danoeh.antennapod.core.export.html; + +import android.text.TextUtils; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import de.danoeh.antennapod.core.export.ExportWriter; +import de.danoeh.antennapod.core.feed.Feed; + +/** Writes HTML documents. */ +public class HtmlWriter implements ExportWriter { + + private static final String TAG = "HtmlWriter"; + private static final String ENCODING = "UTF-8"; + private static final String HTML_TITLE = "AntennaPod Subscriptions"; + + /** + * Takes a list of feeds and a writer and writes those into an HTML + * document. + * + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + */ + @Override + public void writeDocument(List<Feed> feeds, Writer writer) + throws IllegalArgumentException, IllegalStateException, IOException { + Log.d(TAG, "Starting to write document"); + XmlSerializer xs = Xml.newSerializer(); + xs.setFeature(HtmlSymbols.XML_FEATURE_INDENT_OUTPUT, true); + xs.setOutput(writer); + + xs.startDocument(ENCODING, false); + xs.startTag(null, HtmlSymbols.HTML); + xs.startTag(null, HtmlSymbols.HEAD); + xs.startTag(null, HtmlSymbols.TITLE); + xs.text(HTML_TITLE); + xs.endTag(null, HtmlSymbols.TITLE); + xs.endTag(null, HtmlSymbols.HEAD); + + xs.startTag(null, HtmlSymbols.BODY); + xs.startTag(null, HtmlSymbols.HEADING); + xs.text(HTML_TITLE); + xs.endTag(null, HtmlSymbols.HEADING); + xs.startTag(null, HtmlSymbols.ORDERED_LIST); + for (Feed feed : feeds) { + xs.startTag(null, HtmlSymbols.LIST_ITEM); + xs.text(feed.getTitle()); + if (!TextUtils.isEmpty(feed.getLink())) { + xs.text(" ["); + xs.startTag(null, HtmlSymbols.LINK); + xs.attribute(null, HtmlSymbols.LINK_DESTINATION, feed.getLink()); + xs.text("Website"); + xs.endTag(null, HtmlSymbols.LINK); + xs.text("]"); + } + xs.text(" ["); + xs.startTag(null, HtmlSymbols.LINK); + xs.attribute(null, HtmlSymbols.LINK_DESTINATION, feed.getDownload_url()); + xs.text("Feed"); + xs.endTag(null, HtmlSymbols.LINK); + xs.text("]"); + xs.endTag(null, HtmlSymbols.LIST_ITEM); + } + xs.endTag(null, HtmlSymbols.ORDERED_LIST); + xs.endTag(null, HtmlSymbols.BODY); + xs.endTag(null, HtmlSymbols.HTML); + xs.endDocument(); + Log.d(TAG, "Finished writing document"); + } + + public String fileExtension() { + return "html"; + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlElement.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlElement.java index 8d0a4a842..61eb4d0c9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlElement.java +++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlElement.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.core.opml; +package de.danoeh.antennapod.core.export.opml; /** Represents a single feed in an OPML file. */ public class OpmlElement { diff --git a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlReader.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlReader.java index 17afc7904..a17fedd7d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlReader.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.core.opml; +package de.danoeh.antennapod.core.export.opml; import android.util.Log; diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java new file mode 100644 index 000000000..40b0e23b8 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java @@ -0,0 +1,21 @@ +package de.danoeh.antennapod.core.export.opml; + +import de.danoeh.antennapod.core.export.CommonSymbols; + +/** Contains symbols for reading and writing OPML documents. */ +public final class OpmlSymbols extends CommonSymbols { + + public static final String OPML = "opml"; + static final String OUTLINE = "outline"; + static final String TEXT = "text"; + static final String XMLURL = "xmlUrl"; + static final String HTMLURL = "htmlUrl"; + static final String TYPE = "type"; + static final String VERSION = "version"; + static final String DATE_CREATED = "dateCreated"; + + private OpmlSymbols() { + + } + +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java index 673c602df..fd0922f72 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.core.opml; +package de.danoeh.antennapod.core.export.opml; import android.util.Log; import android.util.Xml; @@ -10,11 +10,13 @@ import java.io.Writer; import java.util.Date; import java.util.List; +import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.util.DateUtils; /** Writes OPML documents. */ -public class OpmlWriter { +public class OpmlWriter implements ExportWriter { + private static final String TAG = "OpmlWriter"; private static final String ENCODING = "UTF-8"; private static final String OPML_VERSION = "2.0"; @@ -28,40 +30,29 @@ public class OpmlWriter { * @throws IllegalStateException * @throws IllegalArgumentException */ + @Override public void writeDocument(List<Feed> feeds, Writer writer) throws IllegalArgumentException, IllegalStateException, IOException { Log.d(TAG, "Starting to write document"); XmlSerializer xs = Xml.newSerializer(); + xs.setFeature(OpmlSymbols.XML_FEATURE_INDENT_OUTPUT, true); xs.setOutput(writer); xs.startDocument(ENCODING, false); - xs.text("\n"); xs.startTag(null, OpmlSymbols.OPML); xs.attribute(null, OpmlSymbols.VERSION, OPML_VERSION); - xs.text("\n"); - xs.text(" "); xs.startTag(null, OpmlSymbols.HEAD); - xs.text("\n"); - xs.text(" "); xs.startTag(null, OpmlSymbols.TITLE); xs.text(OPML_TITLE); xs.endTag(null, OpmlSymbols.TITLE); - xs.text("\n"); - xs.text(" "); xs.startTag(null, OpmlSymbols.DATE_CREATED); xs.text(DateUtils.formatRFC822Date(new Date())); xs.endTag(null, OpmlSymbols.DATE_CREATED); - xs.text("\n"); - xs.text(" "); xs.endTag(null, OpmlSymbols.HEAD); - xs.text("\n"); - xs.text(" "); xs.startTag(null, OpmlSymbols.BODY); - xs.text("\n"); for (Feed feed : feeds) { - xs.text(" "); xs.startTag(null, OpmlSymbols.OUTLINE); xs.attribute(null, OpmlSymbols.TEXT, feed.getTitle()); xs.attribute(null, OpmlSymbols.TITLE, feed.getTitle()); @@ -73,14 +64,15 @@ public class OpmlWriter { xs.attribute(null, OpmlSymbols.HTMLURL, feed.getLink()); } xs.endTag(null, OpmlSymbols.OUTLINE); - xs.text("\n"); } - xs.text(" "); xs.endTag(null, OpmlSymbols.BODY); - xs.text("\n"); xs.endTag(null, OpmlSymbols.OPML); - xs.text("\n"); xs.endDocument(); Log.d(TAG, "Finished writing document"); } + + public String fileExtension() { + return "opml"; + } + } diff --git a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlSymbols.java deleted file mode 100644 index c973713cb..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/opml/OpmlSymbols.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.danoeh.antennapod.core.opml; - -/** Contains symbols for reading and writing OPML documents. */ -public final class OpmlSymbols { - - public static final String OPML = "opml"; - public static final String BODY = "body"; - public static final String OUTLINE = "outline"; - public static final String TEXT = "text"; - public static final String XMLURL = "xmlUrl"; - public static final String HTMLURL = "htmlUrl"; - public static final String TYPE = "type"; - public static final String VERSION = "version"; - public static final String HEAD = "head"; - public static final String TITLE = "title"; - public static final String DATE_CREATED = "dateCreated"; - - private OpmlSymbols() { - - } - -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java new file mode 100644 index 000000000..b7ed890f5 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java @@ -0,0 +1,80 @@ +package de.danoeh.antennapod.core.preferences; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; +import android.util.Log; + +import java.util.concurrent.TimeUnit; + +public class SleepTimerPreferences { + + private static final String TAG = "SleepTimerPreferences"; + + private static final String PREF_NAME = "SleepTimerDialog"; + private static final String PREF_VALUE = "LastValue"; + private static final String PREF_TIME_UNIT = "LastTimeUnit"; + private static final String PREF_VIBRATE = "Vibrate"; + private static final String PREF_SHAKE_TO_RESET = "ShakeToReset"; + private static final String PREF_AUTO_ENABLE = "AutoEnable"; + + private static final TimeUnit[] UNITS = { TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS }; + + private static final String DEFAULT_VALUE = "15"; + private static final int DEFAULT_TIME_UNIT = 1; + + private static Context context; + private static SharedPreferences prefs; + + /** + * Sets up the UserPreferences class. + * + * @throws IllegalArgumentException if context is null + */ + public static void init(@NonNull Context context) { + Log.d(TAG, "Creating new instance of SleepTimerPreferences"); + SleepTimerPreferences.prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + } + + public static void setLastTimer(String value, int timeUnit) { + prefs.edit().putString(PREF_VALUE, value).putInt(PREF_TIME_UNIT, timeUnit).apply(); + } + + public static String lastTimerValue() { + return prefs.getString(PREF_VALUE, DEFAULT_VALUE); + } + + public static int lastTimerTimeUnit() { + return prefs.getInt(PREF_TIME_UNIT, DEFAULT_TIME_UNIT); + } + + public static long timerMillis() { + long value = Long.parseLong(lastTimerValue()); + return UNITS[lastTimerTimeUnit()].toMillis(value); + } + + public static void setVibrate(boolean vibrate) { + prefs.edit().putBoolean(PREF_VIBRATE, vibrate).apply(); + } + + public static boolean vibrate() { + return prefs.getBoolean(PREF_VIBRATE, true); + } + + public static void setShakeToReset(boolean shakeToReset) { + prefs.edit().putBoolean(PREF_SHAKE_TO_RESET, shakeToReset).apply(); + } + + public static boolean shakeToReset() { + return prefs.getBoolean(PREF_SHAKE_TO_RESET, true); + } + + public static void setAutoEnable(boolean autoEnable) { + prefs.edit().putBoolean(PREF_AUTO_ENABLE, autoEnable).apply(); + } + + public static boolean autoEnable() { + return prefs.getBoolean(PREF_AUTO_ENABLE, false); + } + +} 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 d5a1007af..aa3bbaeab 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 @@ -56,7 +56,6 @@ public class UserPreferences { public static final String PREF_LOCKSCREEN_BACKGROUND = "prefLockscreenBackground"; public static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport"; - // Queue public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront"; @@ -123,13 +122,14 @@ public class UserPreferences { private static final int NOTIFICATION_BUTTON_FAST_FORWARD = 1; private static final int NOTIFICATION_BUTTON_SKIP = 2; private static int EPISODE_CACHE_SIZE_UNLIMITED = -1; - public static int FEED_ORDER_COUNTER = 0; - public static int FEED_ORDER_ALPHABETICAL = 1; - public static int FEED_ORDER_LAST_UPDATE = 2; - public static int FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM = 0; - public static int FEED_COUNTER_SHOW_NEW = 1; - public static int FEED_COUNTER_SHOW_UNPLAYED = 2; - public static int FEED_COUNTER_SHOW_NONE = 3; + public static final int FEED_ORDER_COUNTER = 0; + public static final int FEED_ORDER_ALPHABETICAL = 1; + public static final int FEED_ORDER_LAST_UPDATE = 2; + public static final int FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM = 0; + public static final int FEED_COUNTER_SHOW_NEW = 1; + public static final int FEED_COUNTER_SHOW_UNPLAYED = 2; + public static final int FEED_COUNTER_SHOW_NONE = 3; + public static final int FEED_COUNTER_SHOW_DOWNLOADED = 4; private static Context context; private static SharedPreferences prefs; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index 0871758d0..cad67539b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java @@ -383,6 +383,9 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { statusBeforeSeeking = playerStatus; setPlayerStatus(PlayerStatus.SEEKING, media, getPosition()); mediaPlayer.seekTo(t); + if (statusBeforeSeeking == PlayerStatus.PREPARED) { + media.setPosition(t); + } try { seekLatch.await(3, TimeUnit.SECONDS); } catch (InterruptedException e) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index e67dc9d0a..33aec0ee0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -47,12 +47,14 @@ import java.util.List; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; import de.danoeh.antennapod.core.storage.DBReader; @@ -62,6 +64,7 @@ import de.danoeh.antennapod.core.util.IntList; import de.danoeh.antennapod.core.util.QueueAccess; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; +import de.greenrobot.event.EventBus; /** * Controls the MediaPlayer that plays a FeedMedia-file @@ -605,6 +608,11 @@ public class PlaybackService extends MediaBrowserServiceCompat { writePlayerStatusPlaybackPreferences(); setupNotification(newInfo); started = true; + // set sleep timer if auto-enabled + if(SleepTimerPreferences.autoEnable() && !sleepTimerActive()) { + setSleepTimer(SleepTimerPreferences.timerMillis(), SleepTimerPreferences.shakeToReset(), + SleepTimerPreferences.vibrate()); + } break; case ERROR: @@ -846,11 +854,14 @@ public class PlaybackService extends MediaBrowserServiceCompat { Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds"); taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate); sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0); + EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label), + () -> disableSleepTimer())); } public void disableSleepTimer() { taskManager.disableSleepTimer(); sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0); + EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label))); } private void writePlaybackPreferencesNoMediaPlaying() { 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 7631d26d5..b0d053f5a 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 @@ -42,12 +42,12 @@ import de.greenrobot.event.EventBus; public class PodDBAdapter { private static final String TAG = "PodDBAdapter"; - public static final String DATABASE_NAME = "Antennapod.db"; + private static final String DATABASE_NAME = "Antennapod.db"; /** * Maximum number of arguments for IN-operator. */ - public static final int IN_OPERATOR_MAXIMUM = 800; + private static final int IN_OPERATOR_MAXIMUM = 800; /** * Maximum number of entries per search request. @@ -109,14 +109,14 @@ public class PodDBAdapter { public static final String KEY_EXCLUDE_FILTER = "exclude_filter"; // Table names - public static final String TABLE_NAME_FEEDS = "Feeds"; - public static final String TABLE_NAME_FEED_ITEMS = "FeedItems"; - public static final String TABLE_NAME_FEED_IMAGES = "FeedImages"; - public static final String TABLE_NAME_FEED_MEDIA = "FeedMedia"; - public static final String TABLE_NAME_DOWNLOAD_LOG = "DownloadLog"; - public static final String TABLE_NAME_QUEUE = "Queue"; - public static final String TABLE_NAME_SIMPLECHAPTERS = "SimpleChapters"; - public static final String TABLE_NAME_FAVORITES = "Favorites"; + private static final String TABLE_NAME_FEEDS = "Feeds"; + private static final String TABLE_NAME_FEED_ITEMS = "FeedItems"; + private static final String TABLE_NAME_FEED_IMAGES = "FeedImages"; + private static final String TABLE_NAME_FEED_MEDIA = "FeedMedia"; + private static final String TABLE_NAME_DOWNLOAD_LOG = "DownloadLog"; + private static final String TABLE_NAME_QUEUE = "Queue"; + private static final String TABLE_NAME_SIMPLECHAPTERS = "SimpleChapters"; + private static final String TABLE_NAME_FAVORITES = "Favorites"; // SQL Statements for creating new tables private static final String TABLE_PRIMARY_KEY = KEY_ID @@ -1436,15 +1436,24 @@ public class PodDBAdapter { public final LongIntMap getFeedCounters(long... feedIds) { int setting = UserPreferences.getFeedCounterSetting(); String whereRead; - if(setting == UserPreferences.FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM) { - whereRead = "(" + KEY_READ + "=" + FeedItem.NEW - + " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")"; - } else if(setting == UserPreferences.FEED_COUNTER_SHOW_NEW) { - whereRead = KEY_READ + "=" + FeedItem.NEW; - } else if(setting == UserPreferences.FEED_COUNTER_SHOW_UNPLAYED) { - whereRead = KEY_READ + "=" + FeedItem.UNPLAYED; - } else { // NONE - return new LongIntMap(0); + switch(setting) { + case UserPreferences.FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM: + whereRead = "(" + KEY_READ + "=" + FeedItem.NEW + + " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")"; + break; + case UserPreferences.FEED_COUNTER_SHOW_NEW: + whereRead = KEY_READ + "=" + FeedItem.NEW; + break; + case UserPreferences.FEED_COUNTER_SHOW_UNPLAYED: + whereRead = KEY_READ + "=" + FeedItem.UNPLAYED; + break; + case UserPreferences.FEED_COUNTER_SHOW_DOWNLOADED: + whereRead = KEY_DOWNLOADED + "=1"; + break; + case UserPreferences.FEED_COUNTER_SHOW_NONE: + // deliberate fall-through + default: // NONE + return new LongIntMap(0); } // work around TextUtils.join wanting only boxed items @@ -1459,8 +1468,10 @@ public class PodDBAdapter { builder.deleteCharAt(builder.length() - 1); } - final String query = "SELECT " + KEY_FEED + ", COUNT(" + KEY_ID + ") AS count " + final String query = "SELECT " + KEY_FEED + ", COUNT(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ") AS count " + " FROM " + TABLE_NAME_FEED_ITEMS + + " LEFT JOIN " + TABLE_NAME_FEED_MEDIA + " ON " + + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + " WHERE " + KEY_FEED + " IN (" + builder.toString() + ") " + " AND " + whereRead + " GROUP BY " + KEY_FEED; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java index 13aadf027..6251cc4a0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java @@ -216,7 +216,7 @@ public abstract class PlaybackController { Intent serviceIntent = new Intent(activity, PlaybackService.class); serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); serviceIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, false); - serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false); + serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, true); boolean fileExists = media.localFileAvailable(); boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream(); if (!fileExists && !lastIsStream && media instanceof FeedMedia) { diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index c8c5162ef..133a3ed6e 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -168,12 +168,14 @@ <item>@string/drawer_feed_counter_new_unplayed</item> <item>@string/drawer_feed_counter_new</item> <item>@string/drawer_feed_counter_unplayed</item> + <item>@string/drawer_feed_counter_downloaded</item> <item>@string/drawer_feed_counter_none</item> </string-array> <string-array name="nav_drawer_feed_counter_values"> <item>0</item> <item>1</item> <item>2</item> + <item>4</item> <item>3</item> </string-array> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index c7042708d..48bd15b13 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -42,6 +42,7 @@ <string name="drawer_feed_counter_new_unplayed">Number of new and unplayed episodes</string> <string name="drawer_feed_counter_new">Number of new episodes</string> <string name="drawer_feed_counter_unplayed">Number of unplayed episodes</string> + <string name="drawer_feed_counter_downloaded">Number of downloaded episodes</string> <string name="drawer_feed_counter_none">None</string> <!-- Webview actions --> @@ -102,7 +103,7 @@ <string name="etxtFeedurlHint">www.example.com/feed</string> <string name="txtvfeedurl_label">Add Podcast by URL</string> <string name="podcastdirectories_label">Find Podcast in Directory</string> - <string name="podcastdirectories_descr">You can search for new podcasts by name, category or popularity in the gpodder.net directory, or search the iTunes store.</string> + <string name="podcastdirectories_descr">For new podcasts, you can search iTunes or fyyd, or browse gpodder.net by name, category or popularity.</string> <string name="browse_gpoddernet_label">Browse gpodder.net</string> <!-- Actions on feeds --> @@ -451,6 +452,7 @@ <string name="choose_file_from_filesystem">From local filesystem</string> <string name="choose_file_from_external_application">Use external application</string> <string name="opml_export_label">OPML export</string> + <string name="html_export_label">HTML export</string> <string name="exporting_label">Exporting…</string> <string name="export_error_label">Export error</string> <string name="opml_export_success_title">OPML Export successful.</string> @@ -482,6 +484,9 @@ <item quantity="one">1 hour</item> <item quantity="other">%d hours</item> </plurals> + <string name="auto_enable_label">Auto-enable</string> + <string name="sleep_timer_enabled_label">Sleep timer enabled</string> + <string name="sleep_timer_disabled_label">Sleep timer disabled</string> <!-- gpodder.net --> <string name="gpodnet_taglist_header">CATEGORIES</string> @@ -575,6 +580,8 @@ <string name="search_itunes_label">Search iTunes</string> <string name="filter">Filter</string> + + <string name="search_fyyd_label">Search fyyd</string> <!-- Episodes apply actions --> <string name="all_label">All</string> diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index 6a4dc4781..9b20fdd02 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -285,6 +285,16 @@ <item name="textAllCaps">false</item> </style> + <style name="Divider"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">1dp</item> + <item name="android:layout_marginTop">8dp</item> + <item name="android:layout_marginLeft">16dp</item> + <item name="android:layout_marginRight">16dp</item> + <item name="android:layout_marginBottom">8dp</item> + <item name="android:background">?android:attr/listDivider</item> + </style> + <style name="AntennaPod.Dialog.Light" parent="Theme.AppCompat.Light.Dialog"> <item name="colorAccent">@color/holo_blue_light</item> </style> diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java index 3dfd6ea65..f12f1d806 100644 --- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java @@ -4,6 +4,7 @@ import android.content.Context; import de.danoeh.antennapod.core.cast.CastManager; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.util.NetworkUtils; @@ -45,6 +46,7 @@ public class ClientConfig { PlaybackPreferences.init(context); NetworkUtils.init(context); CastManager.init(context); + SleepTimerPreferences.init(context); initialized = true; } |