summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2020-05-25 11:34:01 +0200
committerByteHamster <info@bytehamster.com>2020-05-25 11:34:03 +0200
commit382a54028029fe6297ebab405010d7e5229331d4 (patch)
tree3848b3fb636b46534e45bd94a91f2fb97e63e598
parent3ee3ba3f6e770d03791fa3f0ed68e09778291437 (diff)
downloadAntennaPod-382a54028029fe6297ebab405010d7e5229331d4.zip
Basic local feeds support
Co-authored-by: Igor Almeida <igor.contato@gmail.com>
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java38
-rw-r--r--app/src/main/res/layout/addfeed.xml14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java111
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java8
-rw-r--r--core/src/main/res/values/strings.xml3
6 files changed, 175 insertions, 3 deletions
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 546684f14..d29a36871 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -14,16 +14,22 @@ import android.view.ViewGroup;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.Fragment;
+import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.activity.OpmlImportActivity;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.discovery.CombinedSearcher;
import de.danoeh.antennapod.discovery.FyydPodcastSearcher;
import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment;
+import java.util.Collections;
+
/**
* Provides actions for adding new podcast subscriptions.
*/
@@ -31,6 +37,7 @@ public class AddFeedFragment extends Fragment {
public static final String TAG = "AddFeedFragment";
private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
+ private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
private EditText combinedFeedSearchBox;
private MainActivity activity;
@@ -57,8 +64,7 @@ public class AddFeedFragment extends Fragment {
root.findViewById(R.id.btn_add_via_url).setOnClickListener(v
-> showAddViaUrlDialog());
- View butOpmlImport = root.findViewById(R.id.btn_opml_import);
- butOpmlImport.setOnClickListener(v -> {
+ root.findViewById(R.id.btn_opml_import).setOnClickListener(v -> {
try {
Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
@@ -68,6 +74,15 @@ public class AddFeedFragment extends Fragment {
Log.e(TAG, "No activity found. Should never happen...");
}
});
+ root.findViewById(R.id.btn_add_local_folder).setOnClickListener(v -> {
+ try {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ startActivityForResult(intent, REQUEST_CODE_ADD_LOCAL_FOLDER);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ });
root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch());
return root;
}
@@ -127,6 +142,25 @@ public class AddFeedFragment extends Fragment {
Intent intent = new Intent(getContext(), OpmlImportActivity.class);
intent.setData(uri);
startActivity(intent);
+ } else if (requestCode == REQUEST_CODE_ADD_LOCAL_FOLDER) {
+ try {
+ getActivity().getContentResolver()
+ .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ DocumentFile documentFile = DocumentFile.fromTreeUri(getContext(), uri);
+ if (documentFile == null) {
+ throw new IllegalArgumentException("Unable to retrieve document tree");
+ }
+ Feed dirFeed = new Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, documentFile.getName());
+ dirFeed.setDescription(getString(R.string.local_feed_description));
+ dirFeed.setItems(Collections.emptyList());
+ DBTasks.forceRefreshFeed(getContext(), dirFeed, true);
+ ((MainActivity) getActivity())
+ .showSnackbarAbovePlayer(R.string.add_local_folder_success, Snackbar.LENGTH_SHORT);
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ ((MainActivity) getActivity())
+ .showSnackbarAbovePlayer(e.getLocalizedMessage(), Snackbar.LENGTH_LONG);
+ }
}
}
}
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index 9d14d209a..7430579b4 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -101,6 +101,20 @@
android:text="@string/add_podcast_by_url"/>
<TextView
+ android:id="@+id/btn_add_local_folder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawablePadding="8dp"
+ app:drawableStartCompat="?attr/ic_folder"
+ app:drawableLeftCompat="?attr/ic_folder"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:textColor="?android:attr/textColorPrimary"
+ android:clickable="true"
+ android:text="@string/add_local_folder"/>
+
+ <TextView
android:id="@+id/btn_search_itunes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index 0889e5182..9c1c55d76 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -24,6 +24,7 @@ public class Feed extends FeedFile implements ImageResource {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
public static final String TYPE_ATOM1 = "atom";
+ public static final String PREFIX_LOCAL_FOLDER = "antennapod_local:";
/* title as defined by the feed */
private String feedTitle;
@@ -551,4 +552,7 @@ public class Feed extends FeedFile implements ImageResource {
this.lastUpdateFailed = lastUpdateFailed;
}
+ public boolean isLocalFeed() {
+ return download_url.startsWith(PREFIX_LOCAL_FOLDER);
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
new file mode 100644
index 000000000..f024601d5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -0,0 +1,111 @@
+package de.danoeh.antennapod.core.feed;
+
+import android.content.Context;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.util.Log;
+import androidx.documentfile.provider.DocumentFile;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.DownloadError;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+public class LocalFeedUpdater {
+
+ public static void updateFeed(Feed feed, Context context) {
+ String uriString = feed.getDownload_url().replace(Feed.PREFIX_LOCAL_FOLDER, "");
+ DocumentFile documentFolder = DocumentFile.fromTreeUri(context, Uri.parse(uriString));
+ if (documentFolder == null) {
+ reportError(feed, "Unable to retrieve document tree");
+ return;
+ }
+ if (!documentFolder.exists() || !documentFolder.canRead()) {
+ reportError(feed, "Cannot read local directory");
+ return;
+ }
+
+ if (feed.getItems() == null) {
+ feed.setItems(new ArrayList<>());
+ }
+ //make sure it is the latest 'version' of this feed from the db (all items etc)
+ feed = DBTasks.updateFeed(context, feed)[0];
+
+ List<DocumentFile> mediaFiles = new ArrayList<>();
+ for (DocumentFile file : documentFolder.listFiles()) {
+ String mime = file.getType();
+ if (mime != null && (mime.startsWith("audio/") || mime.startsWith("video/"))) {
+ mediaFiles.add(file);
+ }
+ }
+
+ List<FeedItem> newItems = feed.getItems();
+ for (DocumentFile f : mediaFiles) {
+ FeedItem found = feedContainsFile(feed, f.getName());
+ if (found != null) {
+ //TODO make sure the media has not changed (type, duration)
+ } else {
+ FeedItem item = createFeedItem(feed, f, context);
+ newItems.add(item);
+ }
+ }
+
+ List<String> iconLocations = Arrays.asList("folder.jpg", "Folder.jpg", "folder.png", "Folder.png");
+ for (String iconLocation : iconLocations) {
+ DocumentFile image = documentFolder.findFile(iconLocation);
+ if (image != null) {
+ feed.setImageUrl(image.getUri().toString());
+ break;
+ }
+ }
+
+ DBTasks.updateFeed(context, feed);
+ }
+
+ private static FeedItem feedContainsFile(Feed feed, String filename) {
+ List<FeedItem> items = feed.getItems();
+ for (FeedItem i : items) {
+ if (i.getMedia() != null && i.getLink().equals(filename)) {
+ return i;
+ }
+ }
+ return null;
+ }
+
+ private static FeedItem createFeedItem(Feed feed, DocumentFile file, Context context) {
+ //create item
+ long globalId = 0;
+ Date date = new Date();
+ String uuid = UUID.randomUUID().toString();
+ FeedItem item = new FeedItem(globalId, file.getName(), uuid, file.getName(), date, FeedItem.UNPLAYED, feed);
+ item.setAutoDownload(false);
+
+ //add the media to the item
+ long duration = getFileDuration(file, context);
+ long size = file.length();
+ FeedMedia media = new FeedMedia(0, item, (int) duration, 0, size, file.getType(),
+ file.getUri().toString(), file.getUri().toString(), true, null, 0, 0);
+ item.setMedia(media);
+
+ return item;
+ }
+
+ private static void reportError(Feed feed, String reasonDetailed) {
+ DownloadStatus status = new DownloadStatus(feed, feed.getTitle(),
+ DownloadError.ERROR_IO_ERROR, false, reasonDetailed, true);
+ DBWriter.addDownloadStatus(status);
+ DBWriter.setFeedLastUpdateFailed(feed.getId(), true);
+ }
+
+ private static long getFileDuration(DocumentFile f, Context context) {
+ MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
+ mediaMetadataRetriever.setDataSource(context, f.getUri());
+ String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ return Long.parseLong(durationStr);
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 16e2825b4..323e34b6a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -16,6 +16,7 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.sync.SyncService;
@@ -241,7 +242,12 @@ public final class DBTasks {
feed.getPreferences().getUsername(), feed.getPreferences().getPassword());
}
f.setId(feed.getId());
- DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force, initiatedByUser);
+
+ if (f.isLocalFeed()) {
+ new Thread(() -> LocalFeedUpdater.updateFeed(f, context)).start();
+ } else {
+ DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force, initiatedByUser);
+ }
}
/**
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 2827f666e..4f94a141a 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -718,6 +718,9 @@
<string name="discover">Discover</string>
<string name="discover_more">more ยป</string>
<string name="search_powered_by">Search powered by %1$s</string>
+ <string name="add_local_folder">Add local folder</string>
+ <string name="add_local_folder_success">Adding local folder succeeded</string>
+ <string name="local_feed_description">This virtual podcast was created by adding a folder to AntennaPod.</string>
<string name="filter">Filter</string>