From 087f2d7b3f881c6d0efa335d85e7cc62c025381d Mon Sep 17 00:00:00 2001 From: Martin Fietz Date: Fri, 27 Feb 2015 19:59:46 +0100 Subject: [PATCH 01/18] Fix for AntennaPod/AntennaPod#610 Recognize DublinCore date tags --- .../core/syndication/handler/SyndHandler.java | 24 +++++++++--- .../syndication/namespace/NSDublinCore.java | 37 +++++++++++++++++++ .../core/syndication/util/SyndDateUtils.java | 27 ++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java index 1dda24944..47503dee4 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java @@ -1,14 +1,23 @@ package de.danoeh.antennapod.core.syndication.handler; import android.util.Log; -import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.syndication.namespace.*; -import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; +import de.danoeh.antennapod.core.BuildConfig; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.syndication.namespace.NSContent; +import de.danoeh.antennapod.core.syndication.namespace.NSDublinCore; +import de.danoeh.antennapod.core.syndication.namespace.NSITunes; +import de.danoeh.antennapod.core.syndication.namespace.NSMedia; +import de.danoeh.antennapod.core.syndication.namespace.NSRSS20; +import de.danoeh.antennapod.core.syndication.namespace.NSSimpleChapters; +import de.danoeh.antennapod.core.syndication.namespace.Namespace; +import de.danoeh.antennapod.core.syndication.namespace.SyndElement; +import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom; + /** Superclass for all SAX Handlers which process Syndication formats */ public class SyndHandler extends DefaultHandler { private static final String TAG = "SyndHandler"; @@ -100,7 +109,12 @@ public class SyndHandler extends DefaultHandler { state.namespaces.put(uri, new NSMedia()); if (BuildConfig.DEBUG) Log.d(TAG, "Recognized media namespace"); - } + } else if (uri.equals(NSDublinCore.NSURI) + && prefix.equals(NSDublinCore.NSTAG)) { + state.namespaces.put(uri, new NSDublinCore()); + if (BuildConfig.DEBUG) + Log.d(TAG, "Recognized DublinCore namespace"); + } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java new file mode 100644 index 000000000..099593eed --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java @@ -0,0 +1,37 @@ +package de.danoeh.antennapod.core.syndication.namespace; + +import org.xml.sax.Attributes; + +import de.danoeh.antennapod.core.syndication.handler.HandlerState; +import de.danoeh.antennapod.core.syndication.util.SyndDateUtils; + +public class NSDublinCore extends Namespace { + private static final String TAG = "NSDublinCore"; + public static final String NSTAG = "dc"; + public static final String NSURI = "http://purl.org/dc/elements/1.1/"; + + private static final String ITEM = "item"; + private static final String DATE = "date"; + + @Override + public SyndElement handleElementStart(String localName, HandlerState state, + Attributes attributes) { + return new SyndElement(localName, this); + } + + @Override + public void handleElementEnd(String localName, HandlerState state) { + if(state.getTagstack().size() >= 2 + && state.getContentBuf() != null) { + String content = state.getContentBuf().toString(); + SyndElement topElement = state.getTagstack().peek(); + String top = topElement.getName(); + SyndElement secondElement = state.getSecondTag(); + String second = secondElement.getName(); + if (top.equals(DATE) && second.equals(ITEM)) { + state.getCurrentItem().setPubDate( + SyndDateUtils.parseISO8601Date(content)); + } + } + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java index 1ac389f08..a9929d7b1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java @@ -28,6 +28,8 @@ public class SyndDateUtils { */ public static final String RFC3339LOCAL = "yyyy-MM-dd'T'HH:mm:ssZ"; + public static final String ISO8601_SHORT = "yyyy-MM-dd"; + private static ThreadLocal RFC822Formatter = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { @@ -44,6 +46,14 @@ public class SyndDateUtils { }; + private static ThreadLocal ISO8601ShortFormatter = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat(ISO8601_SHORT, Locale.US); + } + + }; + public static Date parseRFC822Date(String date) { Date result = null; if (date.contains("PDT")) { @@ -123,6 +133,23 @@ public class SyndDateUtils { } + public static Date parseISO8601Date(String date) { + if(date.length() > ISO8601_SHORT.length()) { + return parseRFC3339Date(date); + } + Date result = null; + if(date.length() == "YYYYMMDD".length()) { + date = date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6,8); + } + SimpleDateFormat format = ISO8601ShortFormatter.get(); + try { + result = format.parse(date); + } catch (ParseException e) { + e.printStackTrace(); + } + return result; + } + /** * Takes a string of the form [HH:]MM:SS[.mmm] and converts it to * milliseconds. From 66121d606ed07608e61aeb43e4847e9c2df8cbbd Mon Sep 17 00:00:00 2001 From: Martin Fietz Date: Sat, 28 Feb 2015 02:33:19 +0100 Subject: [PATCH 02/18] Implementation of AntennaPod/AntennaPod#623 Instead of having a fixed path, the user can now choose the path to import from via dialog. This made the rather bloated instruction text superfluous. Some minor changes to the DirectoryChooserActivity and the OMPL Import layout. --- .../activity/DirectoryChooserActivity.java | 29 +++++++++++---- .../activity/OpmlImportFromPathActivity.java | 37 +++++++++++++++++-- .../preferences/PreferenceController.java | 7 ++-- app/src/main/res/layout/opml_import.xml | 13 ++++--- 4 files changed, 67 insertions(+), 19 deletions(-) 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..303af3334 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java @@ -15,17 +15,24 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.widget.*; +import android.widget.AdapterView; 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. @@ -37,6 +44,7 @@ public class DirectoryChooserActivity extends ActionBarActivity { public static final String RESULT_SELECTED_DIR = "selected_dir"; public static final int RESULT_CODE_DIR_SELECTED = 1; + public static final String NON_EMPTY_DIRECTORY_WARNING = "warn_non_empty_directory"; private Button butConfirm; private Button butCancel; @@ -52,6 +60,8 @@ public class DirectoryChooserActivity extends ActionBarActivity { private FileObserver fileObserver; + private boolean warnNonEmptyDirectory = false; + @Override protected void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); @@ -65,15 +75,18 @@ public class DirectoryChooserActivity extends ActionBarActivity { txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder); listDirectories = (ListView) findViewById(R.id.directory_list); - butConfirm.setOnClickListener(new OnClickListener() { + if(getIntent().getExtras() != null) { + warnNonEmptyDirectory = getIntent().getExtras().getBoolean(NON_EMPTY_DIRECTORY_WARNING, false); + } + butConfirm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (isValidFile(selectedDir)) { - if (selectedDir.list().length == 0) { - returnSelectedFolder(); + if(warnNonEmptyDirectory && selectedDir.list().length > 0) { + showNonEmptyDirectoryWarning(); } else { - showNonEmptyDirectoryWarning(); + returnSelectedFolder(); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java index 162a8f2e5..07fa6d21d 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity; import android.app.AlertDialog; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -11,20 +12,26 @@ import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStreamReader; +import java.io.Reader; + import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.LangUtils; import de.danoeh.antennapod.core.util.StorageUtils; -import java.io.*; - /** * Lets the user start the OPML-import process from a path */ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { private static final String TAG = "OpmlImportFromPathActivity"; private TextView txtvPath; + private Button butChoose; private Button butStart; private String importPath; @@ -36,9 +43,20 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setContentView(R.layout.opml_import); + butChoose = (Button)findViewById(R.id.butChoosePath); txtvPath = (TextView) findViewById(R.id.txtvPath); butStart = (Button) findViewById(R.id.butStartImport); + butChoose.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startActivityForResult( + new Intent(OpmlImportFromPathActivity.this, + DirectoryChooserActivity.class), + DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED + ); + } + }); butStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -46,13 +64,13 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { } }); + setImportPath(); } @Override protected void onResume() { super.onResume(); StorageUtils.checkStorageAvailability(this); - setImportPath(); } /** @@ -167,5 +185,18 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { dialog.create().show(); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d(TAG, "activity result: " + requestCode + " " + resultCode); + if (requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) { + if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) { + String dir = data + .getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR); + Log.d(TAG, dir); + txtvPath.setText(dir); + importPath = dir.toString(); + } + } + } } 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 ffac05321..a8636874d 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -163,9 +163,10 @@ public class PreferenceController { @Override public boolean onPreferenceClick(Preference preference) { - activity.startActivityForResult( - new Intent(activity, - DirectoryChooserActivity.class), + Intent intent = new Intent(activity, + DirectoryChooserActivity.class); + intent.putExtra(DirectoryChooserActivity.NON_EMPTY_DIRECTORY_WARNING, true); + activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED ); return true; diff --git a/app/src/main/res/layout/opml_import.xml b/app/src/main/res/layout/opml_import.xml index 3e45a0400..1751d0d39 100644 --- a/app/src/main/res/layout/opml_import.xml +++ b/app/src/main/res/layout/opml_import.xml @@ -4,14 +4,16 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" + android:gravity="center" tools:background="@android:color/darker_gray"> - + android:text="@string/choose_data_directory" /> + tools:background="@android:color/holo_green_dark" + android:gravity="center"/>