Merge branch 'develop' into develop
This commit is contained in:
commit
5cbf092f3b
|
@ -4,6 +4,7 @@ host = https://www.transifex.com
|
|||
[antennapod.english]
|
||||
source_file = core/src/main/res/values/strings.xml
|
||||
source_lang = en
|
||||
trans.ast_ES = core/src/main/res/values-b+ast/strings.xml
|
||||
trans.ar = core/src/main/res/values-ar/strings.xml
|
||||
trans.az = core/src/main/res/values-az/strings.xml
|
||||
trans.ca = core/src/main/res/values-ca/strings.xml
|
||||
|
@ -27,6 +28,7 @@ trans.ja = core/src/main/res/values-ja/strings.xml
|
|||
trans.kn_IN = core/src/main/res/values-kn-rIN/strings.xml
|
||||
trans.ko = core/src/main/res/values-ko/strings.xml
|
||||
trans.ko_KR = core/src/main/res/values-ko-rKR/strings.xml
|
||||
trans.lt = core/src/main/res/values-lt/strings.xml
|
||||
trans.nb = core/src/main/res/values-nb/strings.xml
|
||||
trans.no = core/src/main/res/values-no/strings.xml
|
||||
trans.nl = core/src/main/res/values-nl/strings.xml
|
||||
|
@ -42,9 +44,11 @@ trans.ru_RU = core/src/main/res/values-ru/strings.xml
|
|||
trans.uk_UA = core/src/main/res/values-uk-rUA/strings.xml
|
||||
trans.zh_CN = core/src/main/res/values-zh-rCN/strings.xml
|
||||
trans.sv_SE = core/src/main/res/values-sv-rSE/strings.xml
|
||||
trans.te = core/src/main/res/values-te/strings.xml
|
||||
trans.tr = core/src/main/res/values-tr/strings.xml
|
||||
trans.vi = core/src/main/res/values-vi/strings.xml
|
||||
trans.vi_VN = core/src/main/res/values-vi-rVN/strings.xml
|
||||
trans.zh_TW = core/src/main/res/values-zh-rTW/strings.xml
|
||||
|
||||
[antennapod.description]
|
||||
file_filter = app/src/main/play/<lang>/listing/fulldescription
|
||||
|
|
|
@ -0,0 +1,389 @@
|
|||
Change Log
|
||||
==========
|
||||
|
||||
|
||||
Version 1.6.2
|
||||
-------------
|
||||
|
||||
* New features:
|
||||
* Integration of fyyd Podcast Search Engine
|
||||
* Export subscriptions as HTML
|
||||
* Rename feeds
|
||||
* Auto-enable sleep timer
|
||||
* "has media" filter
|
||||
* Force gpodder full sync
|
||||
* Improvements:
|
||||
* Better support for Atom feeds, e.g. summary tag
|
||||
* Confirmation dialog on mark all as seen
|
||||
* Number of downloaded episodes in subscription counter
|
||||
* Gpodder sync error optional
|
||||
* Search results
|
||||
* MRSS support
|
||||
* Sanitize HTML from Atom feed
|
||||
* Fixes:
|
||||
* Reset sleep timer on shake to current waiting time
|
||||
* Cast dialog image
|
||||
* Mini player not showing up
|
||||
* Audio player cover fragment
|
||||
* Prevent out of memory and casting crashes
|
||||
|
||||
Version 1.6.0
|
||||
-------------
|
||||
* New features:
|
||||
* Experimental Chromecast support
|
||||
* Subscription overview
|
||||
* Proxy support
|
||||
* Statistics
|
||||
* Manual gpodder.net sync
|
||||
* Fixes:
|
||||
* Audioplayer controls
|
||||
* Audio ducking
|
||||
* Video control fade-out
|
||||
* External media controls
|
||||
* Feed parsing
|
||||
|
||||
Version 1.5.0
|
||||
-------------
|
||||
* Exclude episodes from auto download by keyword
|
||||
* Configure feeds to prevent them from refreshing automatically
|
||||
* Improved audio player
|
||||
* Improved UI
|
||||
* Bug fixes
|
||||
|
||||
Version 1.4.1
|
||||
-------------
|
||||
* Performance improvements
|
||||
* Hardware buttons now ff and rewind instead of skipping
|
||||
* Option to have forward button skip
|
||||
* Option to send crash reports directly to developers
|
||||
* Highlight currently playing episode
|
||||
* Widget improvements
|
||||
|
||||
Version 1.4.0.12
|
||||
----------------
|
||||
* Fix for crash on Huawei devices (media buttons may not work)
|
||||
|
||||
Version 1.4
|
||||
-----------
|
||||
* BLUETOOTH PERMISSION: Needed to be able to resume playback when a Bluetooth device reconnects with your phone
|
||||
* VIBRATE PERMISSION: Used optionally with the sleep timer
|
||||
* Native variable speed playback (experimental via options)
|
||||
* Improved sleep timer
|
||||
* Mark episodes as 'favorite'
|
||||
* Notification can skip episodes
|
||||
* Keep episodes when skipping them
|
||||
* Episode art on lock screen
|
||||
* Flexible episode cleanup
|
||||
* Rewind after pause
|
||||
* Usability improvements
|
||||
* Bug fixes
|
||||
|
||||
Version 1.3
|
||||
-----------
|
||||
* Bulk actions on feed episodes (download, queue, delete)
|
||||
* Reduced space used by images
|
||||
* Automatic refresh at a certain time of day
|
||||
* Customizable indicators and sorting for feeds
|
||||
* Ability to share feeds
|
||||
* Improved auto download
|
||||
* Many fixes and usability improvements
|
||||
|
||||
Version 1.2
|
||||
-----------
|
||||
* Optionally disable swiping and dragging in the queue
|
||||
* Resume playback after phone call
|
||||
* Filter episodes in the Podcast feed
|
||||
* Hide items in the Nav drawer
|
||||
* Customize times for fast forward and rewind
|
||||
* Resolved issues with opening some OPML files
|
||||
* Various bug fixes and usability improvements
|
||||
|
||||
Version 1.1
|
||||
-----------
|
||||
* iTunes podcast integration
|
||||
* Swipe to remove items from the queue
|
||||
* Set the number of parallel downloads
|
||||
* Fix for gpodder.net on old devices
|
||||
* Fixed date problems for some feeds
|
||||
* Display improvements
|
||||
* Usability improvements
|
||||
* Several other bugfixes
|
||||
|
||||
Version 1.0
|
||||
-----------
|
||||
* The queue can now be sorted
|
||||
* Added option to delete episode after playback
|
||||
* Fixed a bug that caused chapters to be displayed multiple times
|
||||
* Several other improvements and bugfixes
|
||||
|
||||
|
||||
Version 0.9.9.6
|
||||
---------------
|
||||
* Fixed problems related to variable playback speed plugins
|
||||
* Fixed automatic feed update problems
|
||||
* Several other bugfixes and improvements
|
||||
|
||||
Version 0.9.9.5
|
||||
---------------
|
||||
* Added support for paged feeds
|
||||
* Improved user interface
|
||||
* Added Japanese and Turkish translations
|
||||
* Fixed more image loading problems
|
||||
* Other bugfixes and improvements
|
||||
|
||||
Version 0.9.9.4
|
||||
---------------
|
||||
* Added option to keep notification and lockscreen controls when playback is paused
|
||||
* Fixed a bug where episode images were not loaded correctly
|
||||
* Fixed battery usage problems
|
||||
|
||||
Version 0.9.9.3
|
||||
---------------
|
||||
* Fixed video playback problems
|
||||
* Improved image loading
|
||||
* Other bugfixes and improvements
|
||||
|
||||
Version 0.9.9.2
|
||||
---------------
|
||||
* Added support for feed discovery if a website URL is entered
|
||||
* Added support for 'next'/'previous' media keys
|
||||
* Improved sleep timer
|
||||
* Timestamps in shownotes can now be used to jump to a specific position
|
||||
* Automatic Flattring is now configurable
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.9.1
|
||||
---------------
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.9.0
|
||||
---------------
|
||||
* New user interface
|
||||
* Failed downloads are now resumed when restarted
|
||||
* Added support for Podlove Alternate Feeds
|
||||
* Added support for "pcast"-protocol
|
||||
* Added backup & restore functionality. This feature has to be enabled in the Android settings in order to work
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.8.3
|
||||
---------------
|
||||
* Added support for password-protected feeds and episodes
|
||||
* Added support for more types of episode images
|
||||
* Added Hebrew translation
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.8.2
|
||||
---------------
|
||||
* Several bugfixes and improvements
|
||||
* Added Korean translation
|
||||
|
||||
Version 0.9.8.1
|
||||
---------------
|
||||
* Added option to flattr an episode automatically after 80 percent of the episode have been played
|
||||
* Added Polish translation
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.8.0
|
||||
---------------
|
||||
* Added access to the gpodder.net directory
|
||||
* Added ability to sync podcast subscriptions with the gpodder.net service
|
||||
* Automatic download can now be turned on or off for specific podcasts
|
||||
* Added option to pause playback when another app is playing sounds
|
||||
* Added Dutch and Hindi translation
|
||||
* Resolved a problem with automatic podcast updates
|
||||
* Resolved a problem with the buttons' visibility in the episode screen
|
||||
* Resolved a problem where episodes would be re-downloaded unnecessarily
|
||||
* Several other bugfixes and usability improvements
|
||||
|
||||
Version 0.9.7.5
|
||||
---------------
|
||||
* Reduced application startup time
|
||||
* Reduced memory usage
|
||||
* Added option to change the playback speed
|
||||
* Added Swedish translation
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.7.4
|
||||
---------------
|
||||
* Episode cache size can now be set to unlimited
|
||||
* Removing an episode in the queue via sliding can now be undone
|
||||
* Added support for Links in MP3 chapters
|
||||
* Added Czech(Czech Republic), Azerbaijani and Portuguese translations
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.7.3
|
||||
---------------
|
||||
* Bluetooth devices now display metadata during playback (requires AVRCP 1.3 or higher)
|
||||
* User interface improvements
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.7.2
|
||||
---------------
|
||||
* Automatic download can now be disabled
|
||||
* Added Italian (Italy) translation
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.7.1
|
||||
---------------
|
||||
* Added automatic download of new episodes
|
||||
* Added option to specify the number of downloaded episodes to keep on the device
|
||||
* Added support for playback of external media files
|
||||
* Several improvements and bugfixes
|
||||
* Added Catalan translation
|
||||
|
||||
Version 0.9.7
|
||||
-------------
|
||||
* Improved user interface
|
||||
* OPML files can now be imported by selecting them in a file browser
|
||||
* The queue can now be organized via drag & drop
|
||||
* Added expandable notifications (only supported on Android 4.1 and above)
|
||||
* Added Danish, French, Romanian (Romania) and Ukrainian (Ukraine) translation (thanks to all translators!)
|
||||
* Several bugfixes and minor improvements
|
||||
|
||||
Version 0.9.6.4
|
||||
---------------
|
||||
* Added Chinese translation (Thanks tupunco!)
|
||||
* Added Portuguese (Brazil) translation (Thanks mbaltar!)
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.6.3
|
||||
---------------
|
||||
* Added the ability change the location of AntennaPod's data folder
|
||||
* Added Spanish translation (Thanks frandavid100!)
|
||||
* Solved problems with several feeds
|
||||
|
||||
Version 0.9.6.2
|
||||
---------------
|
||||
* Fixed import problems with some OPML files
|
||||
* Fixed download problems
|
||||
* AntennaPod now recognizes changes of episode information
|
||||
* Other improvements and bugfixes
|
||||
|
||||
Version 0.9.6.1
|
||||
---------------
|
||||
* Added dark theme
|
||||
* Several bugfixes and improvements
|
||||
|
||||
Version 0.9.6
|
||||
-------------
|
||||
* Added support for VorbisComment chapters
|
||||
* AntennaPod now shows items as 'in progress' when playback has started
|
||||
* Reduced memory usage
|
||||
* Added support for more feed types
|
||||
* Several bugfixes
|
||||
|
||||
|
||||
Version 0.9.5.3
|
||||
---------------
|
||||
* Fixed crash when trying to start playback on some devices
|
||||
* Fixed problems with some feeds
|
||||
* Other bugfixes and improvements
|
||||
|
||||
Version 0.9.5.2
|
||||
---------------
|
||||
* Media player now doesn't use network bandwidth anymore if not in use
|
||||
* Other improvements and bugfixes
|
||||
|
||||
Version 0.9.5.1
|
||||
---------------
|
||||
* Added playback history
|
||||
* Improved behavior of download report notifications
|
||||
* Improved support for headset controls
|
||||
* Bugfixes in the feed parser
|
||||
* Moved 'OPML import' button into the 'add feed' screen and the 'OPML export' button into the settings screen
|
||||
|
||||
Version 0.9.5
|
||||
-------------
|
||||
* Experimental support for MP3 chapters
|
||||
* New menu options for the 'new' list and the queue
|
||||
* Auto-delete feature
|
||||
* Better Download error reports
|
||||
* Several Bugfixes
|
||||
|
||||
Version 0.9.4.6
|
||||
---------------
|
||||
* Enabled support for small-screen devices
|
||||
* Disabling the sleep timer should now work again
|
||||
|
||||
Version 0.9.4.5
|
||||
---------------
|
||||
* Added Russian translation (Thanks older!)
|
||||
* Added German translation
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.4.4
|
||||
---------------
|
||||
* Added player controls at the bottom of the main screen and the feedlist screens
|
||||
* Improved media playback
|
||||
|
||||
Version 0.9.4.3
|
||||
---------------
|
||||
* Fixed several bugs in the feed parser
|
||||
* Improved behavior of download reports
|
||||
|
||||
Version 0.9.4.2
|
||||
---------------
|
||||
* Fixed bug in the OPML importer
|
||||
* Reduced memory usage of images
|
||||
* Fixed download problems on some devices
|
||||
|
||||
Version 0.9.4.1
|
||||
---------------
|
||||
* Changed behavior of download notifications
|
||||
|
||||
Version 0.9.4
|
||||
-------------
|
||||
* Faster and more reliable downloads
|
||||
* Added lockscreen player controls for Android 4.x devices
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.3.1
|
||||
---------------
|
||||
* Added preference to hide feed items which don't have an episode
|
||||
* Improved image size for some some screen sizes
|
||||
* Added grid view for large screens
|
||||
* Several bugfixes
|
||||
|
||||
Version 0.9.3
|
||||
-------------
|
||||
* MiroGuide integration
|
||||
* Bugfixes in the audio- and videoplayer
|
||||
* Automatically add feeds to the queue when they have been downloaded
|
||||
|
||||
Version 0.9.2
|
||||
-------------
|
||||
* Bugfixes in the user interface
|
||||
* GUID and ID attributes are now recognized by the Feedparser
|
||||
* Stability improvements when adding several feeds at the same time
|
||||
* Fixed bugs that occured when adding certain feeds
|
||||
|
||||
Version 0.9.1.1
|
||||
--------------------
|
||||
* Changed Flattr credentials
|
||||
* Improved layout of Feed information screen
|
||||
* AntennaPod is now open source! The source code is available at https://github.com/danieloeh/AntennaPod
|
||||
|
||||
Version 0.9.1
|
||||
-----------------
|
||||
* Added support for links in SimpleChapters
|
||||
* Bugfix: Current Chapter wasn't always displayed correctly
|
||||
|
||||
Version 0.9
|
||||
--------------
|
||||
|
||||
* OPML export
|
||||
* Flattr integration
|
||||
* Sleep timer
|
||||
|
||||
Version 0.8.2
|
||||
-------------
|
||||
|
||||
* Added search
|
||||
* Improved OPML import experience
|
||||
* More bugfixes
|
||||
|
||||
Version 0.8.1
|
||||
------------
|
||||
|
||||
* Added support for SimpleChapters
|
||||
* OPML import
|
|
@ -21,7 +21,7 @@ AntennaPod has many users and we don't want them to run into trouble when we add
|
|||
|
||||
There are APKs available for every branch that is actively worked on. Please note that these might be very unstable versions of the app, which can break your current installation. Install them at your own risk!
|
||||
|
||||
Click [here](https://www.dropbox.com/sh/4h2kfa2d2jesnu8/AABF7u5QsOn8Lg8MDie4Z4Ama?dl=0) to get to the nightly builds folder.
|
||||
Click [here](https://www.dropbox.com/sh/lzfd640z63qz3fr/AACyxTF1ygR9wMlPLPwVGIUKa?dl=0) to get to the nightly builds folder.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -107,6 +107,10 @@ android {
|
|||
play {
|
||||
}
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
jumboMode true
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
|
@ -139,8 +143,8 @@ dependencies {
|
|||
compile "commons-io:commons-io:$commonsioVersion"
|
||||
compile "org.jsoup:jsoup:$jsoupVersion"
|
||||
compile "com.github.bumptech.glide:glide:$glideVersion"
|
||||
compile "com.squareup.okhttp:okhttp:$okhttpVersion"
|
||||
compile "com.squareup.okhttp:okhttp-urlconnection:$okhttpVersion"
|
||||
compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
|
||||
compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
|
||||
compile "com.squareup.okio:okio:$okioVersion"
|
||||
compile "de.greenrobot:eventbus:$eventbusVersion"
|
||||
compile "io.reactivex:rxandroid:$rxAndroidVersion"
|
||||
|
@ -160,6 +164,8 @@ dependencies {
|
|||
compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
|
||||
|
||||
compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
||||
|
||||
compile 'com.github.mfietz:fyydlin:v0.1'
|
||||
}
|
||||
|
||||
play {
|
||||
|
@ -178,7 +184,8 @@ task filterAbout {
|
|||
from "src/main/templates/about.html"
|
||||
into "src/main/assets"
|
||||
filter(ReplaceTokens, tokens: [versionname: android.defaultConfig.versionName,
|
||||
commit : "git rev-parse --short HEAD".execute().text])
|
||||
commit : "git rev-parse --short HEAD".execute().text,
|
||||
year : new Date().format('yyyy')])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
public *;
|
||||
}
|
||||
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn okio.**
|
||||
|
||||
# for RxJava:
|
||||
|
@ -107,4 +107,19 @@
|
|||
-dontwarn com.viewpagerindicator.LinePageIndicator
|
||||
|
||||
# for some reason ProGuard removes this file. Why? Unsure.
|
||||
-keep class de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider { *; }
|
||||
-keep class de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider { *; }
|
||||
|
||||
# Retrofit 2.0
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
# Moshi
|
||||
-keep class com.squareup.moshi.** { *; }
|
||||
-keep interface com.squareup.moshi.** { *; }
|
||||
-keep public class retrofit2.adapter.rxjava.RxJavaCallAdapterFactory { *; }
|
||||
|
|
|
@ -789,7 +789,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean playingNext) {
|
||||
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public class DBTestUtils {
|
|||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
for (int i = 0; i < numFeeds; i++) {
|
||||
Feed f = new Feed(0, null, "feed " + i, "link" + i, "descr", null, null,
|
||||
Feed f = new Feed(0, null, "feed " + i, null, "link" + i, "descr", null, null,
|
||||
null, null, "id" + i, null, null, "url" + i, false, new FlattrStatus(), false, null, null, false);
|
||||
f.setItems(new ArrayList<>());
|
||||
for (int j = 0; j < numItems; j++) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.danoeh.antennapod"
|
||||
android:versionCode="1060102"
|
||||
android:versionName="1.6.1.2">
|
||||
android:versionCode="1060203"
|
||||
android:versionName="1.6.2.3">
|
||||
<!--
|
||||
Version code schema:
|
||||
"1.2.3-SNAPSHOT" -> 1020300
|
||||
|
@ -37,22 +37,27 @@
|
|||
android:backupAgent=".core.backup.OpmlBackupAgent"
|
||||
android:restoreAnyVersion="true"
|
||||
android:logo="@drawable/ic_launcher">
|
||||
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||
android:resource="@drawable/ic_notification" />
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:name=".activity.SplashActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.AntennaPod.Dark.Splash">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/app_name">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.AudioplayerActivity"
|
||||
android:launchMode="singleTop">
|
||||
|
@ -344,10 +349,6 @@
|
|||
<meta-data
|
||||
android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
|
||||
android:value="GlideModule" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
|
@ -27,6 +28,10 @@ import android.widget.Toast;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.joanzapata.iconify.Iconify;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
|
@ -40,6 +45,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
|
|||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.LangUtils;
|
||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
|
@ -50,17 +56,18 @@ import rx.schedulers.Schedulers;
|
|||
* Displays information about a feed.
|
||||
*/
|
||||
public class FeedInfoActivity extends AppCompatActivity {
|
||||
private static final String TAG = "FeedInfoActivity";
|
||||
private boolean autoDeleteChanged = false;
|
||||
|
||||
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
|
||||
|
||||
private static final String TAG = "FeedInfoActivity";
|
||||
private boolean autoDeleteChanged = false;
|
||||
private Feed feed;
|
||||
|
||||
private ImageView imgvCover;
|
||||
private TextView txtvTitle;
|
||||
private TextView txtvDescription;
|
||||
private TextView lblLanguage;
|
||||
private TextView txtvLanguage;
|
||||
private TextView lblAuthor;
|
||||
private TextView txtvAuthor;
|
||||
private TextView txtvUrl;
|
||||
private EditText etxtUsername;
|
||||
|
@ -75,6 +82,7 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
|
||||
private Subscription subscription;
|
||||
|
||||
|
||||
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -96,6 +104,40 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
}
|
||||
};
|
||||
|
||||
private boolean authInfoChanged = false;
|
||||
|
||||
private TextWatcher authTextWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
authInfoChanged = true;
|
||||
}
|
||||
};
|
||||
|
||||
private boolean filterTextChanged = false;
|
||||
|
||||
private TextWatcher filterTextWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
filterTextChanged = true;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(UserPreferences.getTheme());
|
||||
|
@ -107,7 +149,9 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
imgvCover = (ImageView) findViewById(R.id.imgvCover);
|
||||
txtvTitle = (TextView) findViewById(R.id.txtvTitle);
|
||||
txtvDescription = (TextView) findViewById(R.id.txtvDescription);
|
||||
lblLanguage = (TextView) findViewById(R.id.lblLanguage);
|
||||
txtvLanguage = (TextView) findViewById(R.id.txtvLanguage);
|
||||
lblAuthor = (TextView) findViewById(R.id.lblAuthor);
|
||||
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
|
||||
txtvUrl = (TextView) findViewById(R.id.txtvUrl);
|
||||
cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
|
||||
|
@ -152,14 +196,30 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
.into(imgvCover);
|
||||
|
||||
txtvTitle.setText(feed.getTitle());
|
||||
|
||||
String description = feed.getDescription();
|
||||
txtvDescription.setText((description != null) ? description.trim() : "");
|
||||
if (feed.getAuthor() != null) {
|
||||
txtvAuthor.setText(feed.getAuthor());
|
||||
if(description != null) {
|
||||
if(Feed.TYPE_ATOM1.equals(feed.getType())) {
|
||||
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||
Document feedDescription = Jsoup.parse(feed.getDescription());
|
||||
description = StringUtils.trim(formatter.getPlainText(feedDescription));
|
||||
}
|
||||
} else {
|
||||
description = "";
|
||||
}
|
||||
if (feed.getLanguage() != null) {
|
||||
txtvLanguage.setText(LangUtils
|
||||
.getLanguageString(feed.getLanguage()));
|
||||
txtvDescription.setText(description);
|
||||
|
||||
if (!TextUtils.isEmpty(feed.getAuthor())) {
|
||||
txtvAuthor.setText(feed.getAuthor());
|
||||
} else {
|
||||
lblAuthor.setVisibility(View.GONE);
|
||||
txtvAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(feed.getLanguage())) {
|
||||
txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage()));
|
||||
} else {
|
||||
lblLanguage.setVisibility(View.GONE);
|
||||
txtvLanguage.setVisibility(View.GONE);
|
||||
}
|
||||
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
|
||||
Iconify.addIcons(txtvUrl);
|
||||
|
@ -240,53 +300,6 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if(subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean authInfoChanged = false;
|
||||
|
||||
private TextWatcher authTextWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
authInfoChanged = true;
|
||||
}
|
||||
};
|
||||
|
||||
private boolean filterTextChanged = false;
|
||||
|
||||
private TextWatcher filterTextWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
filterTextChanged = true;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
@ -318,6 +331,14 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if(subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
@ -369,7 +390,7 @@ public class FeedInfoActivity extends AppCompatActivity {
|
|||
private final Feed feed;
|
||||
private final boolean autoDownload;
|
||||
|
||||
public ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
|
||||
ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
|
||||
super(context, R.string.auto_download_apply_to_items_title,
|
||||
R.string.auto_download_apply_to_items_message);
|
||||
this.feed = feed;
|
||||
|
|
|
@ -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;
|
||||
|
@ -46,11 +48,13 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
|||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.Flavors;
|
||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||
|
@ -463,6 +467,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||
protected void onResume() {
|
||||
super.onResume();
|
||||
StorageUtils.checkStorageAvailability(this);
|
||||
DBTasks.checkShouldRefreshFeeds(getApplicationContext());
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent.hasExtra(EXTRA_FEED_ID) ||
|
||||
|
@ -573,6 +578,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||
case R.id.mark_all_read_item:
|
||||
DBWriter.markFeedRead(feed.getId());
|
||||
return true;
|
||||
case R.id.rename_item:
|
||||
new RenameFeedDialog(this, feed).show();
|
||||
return true;
|
||||
case R.id.remove_item:
|
||||
final FeedRemover remover = new FeedRemover(this, feed) {
|
||||
@Override
|
||||
|
@ -731,6 +739,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
|
||||
|
|
|
@ -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;
|
||||
|
@ -43,9 +45,11 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||
import de.danoeh.antennapod.fragment.ChaptersFragment;
|
||||
import de.danoeh.antennapod.fragment.CoverFragment;
|
||||
|
@ -57,6 +61,7 @@ import de.danoeh.antennapod.fragment.QueueFragment;
|
|||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
import de.danoeh.antennapod.preferences.PreferenceController;
|
||||
import de.greenrobot.event.EventBus;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
|
@ -101,6 +106,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
|
|||
|
||||
private Subscription subscription;
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
@ -168,8 +179,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
|
|||
pagerAdapter.onMediaChanged(media);
|
||||
pagerAdapter.setController(controller);
|
||||
}
|
||||
DBTasks.checkShouldRefreshFeeds(getApplicationContext());
|
||||
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
@ -370,6 +383,9 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
|
|||
case R.id.mark_all_read_item:
|
||||
DBWriter.markFeedRead(feed.getId());
|
||||
return true;
|
||||
case R.id.rename_item:
|
||||
new RenameFeedDialog(this, feed).show();
|
||||
return true;
|
||||
case R.id.remove_item:
|
||||
final FeedRemover remover = new FeedRemover(this, feed) {
|
||||
@Override
|
||||
|
@ -462,7 +478,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() {
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.bumptech.glide.Glide;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.examples.HtmlToPlainText;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -63,6 +62,7 @@ import de.danoeh.antennapod.core.util.FileNameGenerator;
|
|||
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||
import de.danoeh.antennapod.core.util.URLChecker;
|
||||
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
|
||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||
import de.greenrobot.event.EventBus;
|
||||
import rx.Observable;
|
||||
|
@ -81,17 +81,12 @@ import rx.schedulers.Schedulers;
|
|||
*/
|
||||
public class OnlineFeedViewActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "OnlineFeedViewActivity";
|
||||
|
||||
public static final String ARG_FEEDURL = "arg.feedurl";
|
||||
|
||||
// Optional argument: specify a title for the actionbar.
|
||||
public static final String ARG_TITLE = "title";
|
||||
|
||||
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
|
||||
|
||||
public static final int RESULT_ERROR = 2;
|
||||
|
||||
private static final String TAG = "OnlineFeedViewActivity";
|
||||
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
|
||||
private volatile List<Feed> feeds;
|
||||
private Feed feed;
|
||||
private String selectedDownloadUrl;
|
||||
|
@ -106,17 +101,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
private Subscription download;
|
||||
private Subscription parser;
|
||||
private Subscription updater;
|
||||
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
setSubscribeButtonState(feed);
|
||||
}
|
||||
|
||||
private EventDistributor.EventListener listener = new EventDistributor.EventListener() {
|
||||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
|
||||
updater = Observable.fromCallable(() -> DBReader.getFeedList())
|
||||
updater = Observable.fromCallable(DBReader::getFeedList)
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
|
@ -133,6 +122,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
}
|
||||
};
|
||||
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
setSubscribeButtonState(feed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(UserPreferences.getTheme());
|
||||
|
@ -284,7 +278,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
})
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(status -> checkDownloadResult(status),
|
||||
.subscribe(this::checkDownloadResult,
|
||||
error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
|
||||
|
@ -360,14 +354,19 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
* This method is executed on a background thread
|
||||
*/
|
||||
private void beforeShowFeedInformation(Feed feed) {
|
||||
// remove HTML tags from descriptions
|
||||
final HtmlToPlainText formatter = new HtmlToPlainText();
|
||||
if(Feed.TYPE_ATOM1.equals(feed.getType()) && feed.getDescription() != null) {
|
||||
// remove HTML tags from descriptions
|
||||
Log.d(TAG, "Removing HTML from feed description");
|
||||
Document feedDescription = Jsoup.parse(feed.getDescription());
|
||||
feed.setDescription(StringUtils.trim(formatter.getPlainText(feedDescription)));
|
||||
}
|
||||
Log.d(TAG, "Removing HTML from shownotes");
|
||||
if (feed.getItems() != null) {
|
||||
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||
for (FeedItem item : feed.getItems()) {
|
||||
if (item.getDescription() != null) {
|
||||
Document description = Jsoup.parse(item.getDescription());
|
||||
item.setDescription(StringUtils.trim(formatter.getPlainText(description)));
|
||||
Document itemDescription = Jsoup.parse(item.getDescription());
|
||||
item.setDescription(StringUtils.trim(formatter.getPlainText(itemDescription)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -589,7 +588,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|
|||
|
||||
private String feedUrl;
|
||||
|
||||
public FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
|
||||
FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
|
||||
super(context, titleRes, true, false, null, null);
|
||||
this.feedUrl = feedUrl;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.danoeh.antennapod.activity;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
|
@ -20,7 +21,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 +30,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;
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,8 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
|
|||
return;
|
||||
}
|
||||
this.uri = uri;
|
||||
if(uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
|
||||
int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermission();
|
||||
|
|
|
@ -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/>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
@ -60,6 +58,12 @@ public class PreferenceActivityGingerbread extends android.preference.Preference
|
|||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
preferenceController.onStop();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
|
||||
theme.applyStyle(UserPreferences.getTheme(), true);
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package de.danoeh.antennapod.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
/**
|
||||
* Creator: vbarad
|
||||
* Date: 2016-12-03
|
||||
* Project: AntennaPod
|
||||
*/
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ public class ActionButtonUtils {
|
|||
* Sets the displayed bitmap and content description of the given
|
||||
* action button so that it matches the state of the FeedItem.
|
||||
*/
|
||||
@SuppressWarnings("ResourceType")
|
||||
public void configureActionButton(ImageButton butSecondary, FeedItem item, boolean isInQueue) {
|
||||
Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
|
||||
|
||||
|
@ -57,8 +58,7 @@ public class ActionButtonUtils {
|
|||
if (isDownloadingMedia) {
|
||||
// item is being downloaded
|
||||
butSecondary.setVisibility(View.VISIBLE);
|
||||
butSecondary.setImageDrawable(drawables
|
||||
.getDrawable(1));
|
||||
butSecondary.setImageDrawable(drawables.getDrawable(1));
|
||||
butSecondary.setContentDescription(context.getString(labels[1]));
|
||||
} else {
|
||||
// item is not downloaded and not being downloaded
|
||||
|
|
|
@ -15,7 +15,7 @@ import de.danoeh.antennapod.core.util.NetworkUtils;
|
|||
/**
|
||||
* Utility methods for adapters
|
||||
*/
|
||||
public class AdapterUtils {
|
||||
class AdapterUtils {
|
||||
|
||||
private static final String TAG = AdapterUtils.class.getSimpleName();
|
||||
|
||||
|
@ -26,7 +26,7 @@ public class AdapterUtils {
|
|||
/**
|
||||
* Updates the contents of the TextView that shows the current playback position and the ProgressBar.
|
||||
*/
|
||||
public static void updateEpisodePlaybackProgress(FeedItem item, TextView txtvPos, ProgressBar episodeProgress) {
|
||||
static void updateEpisodePlaybackProgress(FeedItem item, TextView txtvPos, ProgressBar episodeProgress) {
|
||||
FeedMedia media = item.getMedia();
|
||||
episodeProgress.setVisibility(View.GONE);
|
||||
if (media == null) {
|
||||
|
@ -47,7 +47,6 @@ public class AdapterUtils {
|
|||
- media.getPosition()));
|
||||
}
|
||||
} else if (!media.isDownloaded()) {
|
||||
Log.d(TAG, "size: " + media.getSize());
|
||||
if (media.getSize() > 0) {
|
||||
txtvPos.setText(Converter.byteToString(media.getSize()));
|
||||
} else if(NetworkUtils.isDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
|
||||
|
|
|
@ -87,6 +87,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ResourceType")
|
||||
public View getView(final int position, View convertView, ViewGroup parent) {
|
||||
Holder holder;
|
||||
final FeedItem item = getItem(position);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -86,35 +89,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
|
|||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -225,6 +225,10 @@ public class EpisodesApplyActionFragment extends Fragment {
|
|||
checkQueued(false);
|
||||
resId = R.string.selected_not_queued_label;
|
||||
break;
|
||||
case R.id.check_has_media:
|
||||
checkWithMedia();
|
||||
resId = R.string.selected_has_media_label;
|
||||
break;
|
||||
case R.id.sort_title_a_z:
|
||||
sortByTitle(false);
|
||||
return true;
|
||||
|
@ -357,6 +361,17 @@ public class EpisodesApplyActionFragment extends Fragment {
|
|||
refreshCheckboxes();
|
||||
}
|
||||
|
||||
private void checkWithMedia() {
|
||||
for (FeedItem episode : episodes) {
|
||||
if(episode.hasMedia()) {
|
||||
checkedIds.add(episode.getId());
|
||||
} else {
|
||||
checkedIds.remove(episode.getId());
|
||||
}
|
||||
}
|
||||
refreshCheckboxes();
|
||||
}
|
||||
|
||||
private void refreshTitles() {
|
||||
titles.clear();
|
||||
for(FeedItem episode : episodes) {
|
||||
|
|
|
@ -18,10 +18,6 @@ import android.widget.TextView;
|
|||
import com.afollestad.materialdialogs.DialogAction;
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.afollestad.materialdialogs.internal.MDButton;
|
||||
import com.squareup.okhttp.Credentials;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -33,6 +29,10 @@ import de.danoeh.antennapod.R;
|
|||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
import de.danoeh.antennapod.core.service.download.ProxyConfig;
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import rx.Observable;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
|
@ -258,10 +258,11 @@ public class ProxyDialog {
|
|||
SocketAddress address = InetSocketAddress.createUnresolved(host, portValue);
|
||||
Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase());
|
||||
Proxy proxy = new Proxy(proxyType, address);
|
||||
OkHttpClient client = AntennapodHttpClient.newHttpClient();
|
||||
client.setConnectTimeout(10, TimeUnit.SECONDS);
|
||||
client.setProxy(proxy);
|
||||
client.interceptors().clear();
|
||||
OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.proxy(proxy);
|
||||
builder.interceptors().clear();
|
||||
OkHttpClient client = builder.build();
|
||||
if(!TextUtils.isEmpty(username)) {
|
||||
String credentials = Credentials.basic(username, password);
|
||||
client.interceptors().add(chain -> {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package de.danoeh.antennapod.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.text.InputType;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
|
||||
public class RenameFeedDialog {
|
||||
|
||||
private final WeakReference<Activity> activityRef;
|
||||
private final Feed feed;
|
||||
|
||||
public RenameFeedDialog(Activity activity, Feed feed) {
|
||||
this.activityRef = new WeakReference<>(activity);
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
Activity activity = activityRef.get();
|
||||
if(activity == null) {
|
||||
return;
|
||||
}
|
||||
new MaterialDialog.Builder(activity)
|
||||
.title(de.danoeh.antennapod.core.R.string.rename_feed_label)
|
||||
.inputType(InputType.TYPE_CLASS_TEXT)
|
||||
.input(feed.getTitle(), feed.getTitle(), true, (dialog, input) -> {
|
||||
feed.setCustomTitle(input.toString());
|
||||
DBWriter.setFeedCustomTitle(feed);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.neutralText(de.danoeh.antennapod.core.R.string.reset)
|
||||
.onNeutral((dialog, which) -> dialog.getInputEditText().setText(feed.getFeedTitle()))
|
||||
.negativeText(de.danoeh.antennapod.core.R.string.cancel_label)
|
||||
.onNegative((dialog, which) -> dialog.dismiss())
|
||||
.autoDismiss(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package de.danoeh.antennapod.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
|
@ -9,6 +9,7 @@ import android.view.View;
|
|||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
@ -16,36 +17,27 @@ 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.event.MessageEvent;
|
||||
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
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) {
|
||||
protected SleepTimerDialog(Context context) {
|
||||
this.context = context;
|
||||
prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public MaterialDialog createNewDialog() {
|
||||
|
@ -58,7 +50,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 +67,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,12 +97,17 @@ 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());
|
||||
|
||||
chAutoEnable.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||
SleepTimerPreferences.setAutoEnable(isChecked);
|
||||
int messageString = isChecked ? R.string.sleep_timer_enabled_label : R.string.sleep_timer_disabled_label;
|
||||
EventBus.getDefault().post(new MessageEvent(context.getString(messageString)));
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
@ -125,19 +123,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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
||||
|
|
|
@ -227,22 +227,32 @@ public class AllEpisodesFragment extends Fragment {
|
|||
}
|
||||
return true;
|
||||
case R.id.mark_all_read_item:
|
||||
ConfirmationDialog conDialog = new ConfirmationDialog(getActivity(),
|
||||
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
|
||||
R.string.mark_all_read_label,
|
||||
R.string.mark_all_read_confirmation_msg) {
|
||||
|
||||
@Override
|
||||
public void onConfirmButtonPressed(
|
||||
DialogInterface dialog) {
|
||||
public void onConfirmButtonPressed(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
DBWriter.markAllItemsRead();
|
||||
Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
};
|
||||
conDialog.createNewDialog().show();
|
||||
markAllReadConfirmationDialog.createNewDialog().show();
|
||||
return true;
|
||||
case R.id.mark_all_seen_item:
|
||||
DBWriter.markNewItemsSeen();
|
||||
ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
|
||||
R.string.mark_all_seen_label,
|
||||
R.string.mark_all_seen_confirmation_msg) {
|
||||
|
||||
@Override
|
||||
public void onConfirmButtonPressed(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
DBWriter.markNewItemsSeen();
|
||||
Toast.makeText(getActivity(), R.string.mark_all_seen_msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
};
|
||||
markAllSeenConfirmationDialog.createNewDialog().show();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -69,16 +69,18 @@ public class ChaptersFragment extends ListFragment implements MediaplayerInfoCon
|
|||
|
||||
@Override
|
||||
public void onMediaChanged(Playable media) {
|
||||
if(this.media == media || adapter == null) {
|
||||
if(this.media == media) {
|
||||
return;
|
||||
}
|
||||
this.media = media;
|
||||
adapter.setMedia(media);
|
||||
adapter.notifyDataSetChanged();
|
||||
if(media == null || media.getChapters() == null || media.getChapters().size() == 0) {
|
||||
setEmptyText(getString(R.string.no_items_label));
|
||||
} else {
|
||||
setEmptyText(null);
|
||||
if (adapter != null) {
|
||||
adapter.setMedia(media);
|
||||
adapter.notifyDataSetChanged();
|
||||
if(media == null || media.getChapters() == null || media.getChapters().size() == 0) {
|
||||
setEmptyText(getString(R.string.no_items_label));
|
||||
} else {
|
||||
setEmptyText(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,22 +33,15 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
|
|||
|
||||
public static CoverFragment newInstance(Playable item) {
|
||||
CoverFragment f = new CoverFragment();
|
||||
if (item != null) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARG_PLAYABLE, item);
|
||||
f.setArguments(args);
|
||||
}
|
||||
f.media = item;
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
media = args.getParcelable(ARG_PLAYABLE);
|
||||
} else {
|
||||
Log.e(TAG, TAG + " was called with invalid arguments");
|
||||
if (media == null) {
|
||||
Log.e(TAG, TAG + " was called without media");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,11 +91,13 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
|
|||
|
||||
@Override
|
||||
public void onMediaChanged(Playable media) {
|
||||
if(!isAdded() || this.media == media) {
|
||||
if(this.media == media) {
|
||||
return;
|
||||
}
|
||||
this.media = media;
|
||||
loadMediaInfo();
|
||||
if (isAdded()) {
|
||||
loadMediaInfo();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
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.core.service.download.AntennapodHttpClient;
|
||||
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(AntennapodHttpClient.getHttpClient());
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -185,8 +185,10 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
|
|||
super.onViewCreated(view, savedInstanceState);
|
||||
Bundle args = getArguments();
|
||||
if (args.containsKey(ARG_PLAYABLE)) {
|
||||
media = args.getParcelable(ARG_PLAYABLE);
|
||||
shownotesProvider = media;
|
||||
if (media == null) {
|
||||
media = args.getParcelable(ARG_PLAYABLE);
|
||||
shownotesProvider = media;
|
||||
}
|
||||
load();
|
||||
} else if (args.containsKey(ARG_FEEDITEM_ID)) {
|
||||
long id = getArguments().getLong(ARG_FEEDITEM_ID);
|
||||
|
@ -377,12 +379,14 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
|
|||
|
||||
@Override
|
||||
public void onMediaChanged(Playable media) {
|
||||
if(this.media == media || webvDescription == null) {
|
||||
if(this.media == media) {
|
||||
return;
|
||||
}
|
||||
this.media = media;
|
||||
this.shownotesProvider = media;
|
||||
load();
|
||||
if (webvDescription != null) {
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
|
|||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
|
||||
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
|
@ -99,6 +100,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
|
||||
private boolean itemsLoaded = false;
|
||||
private boolean viewsCreated = false;
|
||||
private boolean headerCreated = false;
|
||||
|
||||
private List<Downloader> downloaderList;
|
||||
|
||||
|
@ -106,6 +108,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
|
||||
private boolean isUpdatingFeed;
|
||||
|
||||
private TextView txtvTitle;
|
||||
private IconTextView txtvFailure;
|
||||
|
||||
private TextView txtvInformation;
|
||||
|
@ -248,6 +251,9 @@ public class ItemlistFragment extends ListFragment {
|
|||
.newInstance(feed.getItems());
|
||||
((MainActivity)getActivity()).loadChildFragment(fragment);
|
||||
return true;
|
||||
case R.id.rename_item:
|
||||
new RenameFeedDialog(getActivity(), feed).show();
|
||||
return true;
|
||||
case R.id.remove_item:
|
||||
final FeedRemover remover = new FeedRemover(
|
||||
getActivity(), feed) {
|
||||
|
@ -415,6 +421,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((EVENTS & arg) != 0) {
|
||||
Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
|
||||
refreshHeaderView();
|
||||
loadItems();
|
||||
updateProgressBarVisibility();
|
||||
}
|
||||
|
@ -460,8 +467,8 @@ public class ItemlistFragment extends ListFragment {
|
|||
}
|
||||
|
||||
private void refreshHeaderView() {
|
||||
if (getListView() == null || feed == null) {
|
||||
Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
|
||||
if (getListView() == null || feed == null || !headerCreated) {
|
||||
Log.e(TAG, "Unable to refresh header view");
|
||||
return;
|
||||
}
|
||||
if(feed.hasLastUpdateFailed()) {
|
||||
|
@ -469,6 +476,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
} else {
|
||||
txtvFailure.setVisibility(View.GONE);
|
||||
}
|
||||
txtvTitle.setText(feed.getTitle());
|
||||
if(feed.getItemFilter() != null) {
|
||||
FeedItemFilter filter = feed.getItemFilter();
|
||||
if(filter.getValues().length > 0) {
|
||||
|
@ -498,7 +506,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
View header = inflater.inflate(R.layout.feeditemlist_header, lv, false);
|
||||
lv.addHeaderView(header);
|
||||
|
||||
TextView txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
|
||||
txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
|
||||
TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
|
||||
ImageView imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground);
|
||||
ImageView imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
|
||||
|
@ -539,6 +547,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
startActivity(startIntent);
|
||||
}
|
||||
});
|
||||
headerCreated = true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ import android.widget.ProgressBar;
|
|||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
@ -39,6 +36,9 @@ import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
|
|||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
|
|
|
@ -26,6 +26,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
|||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
|
@ -102,7 +103,7 @@ public class SubscriptionFragment extends Fragment {
|
|||
if(subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
subscription = Observable.fromCallable(() -> DBReader.getNavDrawerData())
|
||||
subscription = Observable.fromCallable(DBReader::getNavDrawerData)
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
|
@ -164,6 +165,9 @@ public class SubscriptionFragment extends Fragment {
|
|||
.subscribe(result -> loadSubscriptions(),
|
||||
error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
return true;
|
||||
case R.id.rename_item:
|
||||
new RenameFeedDialog(getActivity(), feed).show();
|
||||
return true;
|
||||
case R.id.remove_item:
|
||||
final FeedRemover remover = new FeedRemover(getContext(), feed) {
|
||||
@Override
|
||||
|
|
|
@ -28,6 +28,7 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResourceType")
|
||||
public static void refreshLockItem(Context context, Menu menu) {
|
||||
final MenuItem queueLock = menu.findItem(de.danoeh.antennapod.R.id.queue_lock);
|
||||
int[] lockIcons = new int[] { de.danoeh.antennapod.R.attr.ic_lock_open, de.danoeh.antennapod.R.attr.ic_lock_closed };
|
||||
|
|
|
@ -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;
|
||||
|
@ -42,6 +43,8 @@ import java.io.File;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -55,7 +58,10 @@ import de.danoeh.antennapod.activity.MediaplayerActivity;
|
|||
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;
|
||||
|
@ -67,6 +73,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.
|
||||
|
@ -76,55 +86,36 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
|
||||
private static final String TAG = "PreferenceController";
|
||||
|
||||
public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
|
||||
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
|
||||
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
|
||||
public static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
|
||||
public static final String PREF_OPML_EXPORT = "prefOpmlExport";
|
||||
public static final String STATISTICS = "statistics";
|
||||
public static final String PREF_ABOUT = "prefAbout";
|
||||
public static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
|
||||
public static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
|
||||
public static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
|
||||
private static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
|
||||
private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
|
||||
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";
|
||||
private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
|
||||
private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
|
||||
public static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
|
||||
public static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
|
||||
public static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
|
||||
public static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
|
||||
public static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
|
||||
public static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
|
||||
public static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
|
||||
public static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
|
||||
public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
|
||||
public static final String PREF_PROXY = "prefProxy";
|
||||
public static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
|
||||
public static final String PREF_FAQ = "prefFaq";
|
||||
public static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
|
||||
|
||||
private final PreferenceUI ui;
|
||||
|
||||
private CheckBoxPreference[] selectedNetworks;
|
||||
|
||||
private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
|
||||
private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
|
||||
private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
|
||||
private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
|
||||
private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
|
||||
private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
|
||||
private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
|
||||
private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
|
||||
private static final String PREF_PROXY = "prefProxy";
|
||||
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 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)) {
|
||||
|
@ -132,6 +123,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.
|
||||
|
@ -146,6 +145,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();
|
||||
|
||||
|
@ -181,11 +190,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 &&
|
||||
|
@ -361,6 +368,18 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
toast.show();
|
||||
return true;
|
||||
});
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_FORCE_FULL_SYNC).
|
||||
setOnPreferenceClickListener(preference -> {
|
||||
GpodnetPreferences.setLastSubscriptionSyncTimestamp(0L);
|
||||
GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(0L);
|
||||
GpodnetPreferences.setLastSyncAttempt(false, 0);
|
||||
updateLastGpodnetSyncReport(false, 0);
|
||||
GpodnetSyncService.sendSyncIntent(ui.getActivity().getApplicationContext());
|
||||
Toast toast = Toast.makeText(ui.getActivity(), R.string.pref_gpodnet_sync_started,
|
||||
Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
return true;
|
||||
});
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
|
||||
preference -> {
|
||||
GpodnetPreferences.logout();
|
||||
|
@ -440,6 +459,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 +517,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 &&
|
||||
|
@ -505,6 +564,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
ui.findPreference(PreferenceController.PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_SYNC).setEnabled(loggedIn);
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
|
||||
ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
|
||||
ui.findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
|
||||
if(loggedIn) {
|
||||
|
@ -524,7 +584,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
|
||||
Preference sync = ui.findPreference(PREF_GPODNET_SYNC);
|
||||
if (lastTime != 0) {
|
||||
sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_sum) + "\n" +
|
||||
sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_changes_sum) + "\n" +
|
||||
ui.getActivity().getString(R.string.pref_gpodnet_sync_sum_last_sync_line,
|
||||
ui.getActivity().getString(successful ?
|
||||
R.string.gpodnetsync_pref_report_successful :
|
||||
|
@ -535,7 +595,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
DateUtils.WEEK_IN_MILLIS,
|
||||
DateUtils.FORMAT_SHOW_TIME)));
|
||||
} else {
|
||||
sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_sum));
|
||||
sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,6 +763,12 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
|
||||
|
||||
if (networks != null) {
|
||||
Collections.sort(networks, new Comparator<WifiConfiguration>() {
|
||||
@Override
|
||||
public int compare(WifiConfiguration x, WifiConfiguration y) {
|
||||
return x.SSID.compareTo(y.SSID);
|
||||
}
|
||||
});
|
||||
selectedNetworks = new CheckBoxPreference[networks.size()];
|
||||
List<String> prefValues = Arrays.asList(UserPreferences
|
||||
.getAutodownloadSelectedNetworks());
|
||||
|
@ -787,9 +853,8 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
|
||||
UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
|
||||
});
|
||||
builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
|
||||
UserPreferences.setHiddenDrawerItems(hiddenDrawerItems));
|
||||
builder.setNegativeButton(R.string.cancel_label, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
@ -833,9 +898,8 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
|
|||
preferredButtons.remove((Integer) which);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
|
||||
UserPreferences.setCompactNotificationButtons(preferredButtons);
|
||||
});
|
||||
builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
|
||||
UserPreferences.setCompactNotificationButtons(preferredButtons));
|
||||
builder.setNegativeButton(R.string.cancel_label, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
|
||||
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
|
||||
|
||||
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
|
||||
|
||||
<b>All features:</b><br>
|
||||
IMPORT, ORGANIZE AND PLAY<br>
|
||||
• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
|
||||
• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
|
||||
• Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
|
||||
• Access password-protected feeds and episodes<br>
|
||||
• Take advantage of paged feeds (www.podlove.org/paged-feeds)
|
||||
|
||||
KEEP TRACK, SHARE & APPRECIATE<br>
|
||||
• Keep track of the best of the best by marking episodes as favourites<br>
|
||||
• Find that one episode through the playback history or by searching (titles and shownotes)<br>
|
||||
• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
|
||||
• Support content creators with Flattr integration including automatic flattring
|
||||
|
||||
CONTROL THE SYSTEM<br>
|
||||
• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
|
||||
• Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
|
||||
• Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
|
||||
• Adapt to your environment using the light and dark theme<br>
|
||||
• Back-up your subscriptions with the gPodder.net integration and OPML export
|
||||
|
||||
<b>Join the AntennaPod community!</b><br>
|
||||
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
|
||||
|
||||
GitHub is the place to go for feature requests, bug reports and code contributions:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod
|
||||
|
||||
Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
|
||||
https://groups.google.com/forum/#!forum/antennapod
|
||||
|
||||
Have a question or want to give us feedback?
|
||||
https://twitter.com/@AntennaPod
|
||||
|
||||
Transifex is the place to help with translations:<br>
|
||||
https://www.transifex.com/antennapod/antennapod
|
||||
|
||||
Check out our Beta Testing programme to get the latest features first:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
|
|
@ -0,0 +1,43 @@
|
|||
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
|
||||
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
|
||||
|
||||
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
|
||||
|
||||
<b>All features:</b><br>
|
||||
IMPORT, ORGANIZE AND PLAY<br>
|
||||
• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
|
||||
• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
|
||||
• Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
|
||||
• Access password-protected feeds and episodes<br>
|
||||
• Take advantage of paged feeds (www.podlove.org/paged-feeds)
|
||||
|
||||
KEEP TRACK, SHARE & APPRECIATE<br>
|
||||
• Keep track of the best of the best by marking episodes as favourites<br>
|
||||
• Find that one episode through the playback history or by searching (titles and shownotes)<br>
|
||||
• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
|
||||
• Support content creators with Flattr integration including automatic flattring
|
||||
|
||||
CONTROL THE SYSTEM<br>
|
||||
• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
|
||||
• Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
|
||||
• Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
|
||||
• Adapt to your environment using the light and dark theme<br>
|
||||
• Back-up your subscriptions with the gPodder.net integration and OPML export
|
||||
|
||||
<b>Join the AntennaPod community!</b><br>
|
||||
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
|
||||
|
||||
GitHub is the place to go for feature requests, bug reports and code contributions:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod
|
||||
|
||||
Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
|
||||
https://groups.google.com/forum/#!forum/antennapod
|
||||
|
||||
Have a question or want to give us feedback?
|
||||
https://twitter.com/@AntennaPod
|
||||
|
||||
Transifex is the place to help with translations:<br>
|
||||
https://www.transifex.com/antennapod/antennapod
|
||||
|
||||
Check out our Beta Testing programme to get the latest features first:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
|
|
@ -0,0 +1,43 @@
|
|||
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
|
||||
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
|
||||
|
||||
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
|
||||
|
||||
<b>All features:</b><br>
|
||||
IMPORT, ORGANIZE AND PLAY<br>
|
||||
• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
|
||||
• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
|
||||
• Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
|
||||
• Access password-protected feeds and episodes<br>
|
||||
• Take advantage of paged feeds (www.podlove.org/paged-feeds)
|
||||
|
||||
KEEP TRACK, SHARE & APPRECIATE<br>
|
||||
• Keep track of the best of the best by marking episodes as favourites<br>
|
||||
• Find that one episode through the playback history or by searching (titles and shownotes)<br>
|
||||
• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
|
||||
• Support content creators with Flattr integration including automatic flattring
|
||||
|
||||
CONTROL THE SYSTEM<br>
|
||||
• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
|
||||
• Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
|
||||
• Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
|
||||
• Adapt to your environment using the light and dark theme<br>
|
||||
• Back-up your subscriptions with the gPodder.net integration and OPML export
|
||||
|
||||
<b>Join the AntennaPod community!</b><br>
|
||||
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
|
||||
|
||||
GitHub is the place to go for feature requests, bug reports and code contributions:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod
|
||||
|
||||
Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
|
||||
https://groups.google.com/forum/#!forum/antennapod
|
||||
|
||||
Have a question or want to give us feedback?
|
||||
https://twitter.com/@AntennaPod
|
||||
|
||||
Transifex is the place to help with translations:<br>
|
||||
https://www.transifex.com/antennapod/antennapod
|
||||
|
||||
Check out our Beta Testing programme to get the latest features first:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
|
|
@ -0,0 +1,43 @@
|
|||
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
|
||||
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
|
||||
|
||||
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
|
||||
|
||||
<b>All features:</b><br>
|
||||
IMPORT, ORGANIZE AND PLAY<br>
|
||||
• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
|
||||
• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
|
||||
• Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
|
||||
• Access password-protected feeds and episodes<br>
|
||||
• Take advantage of paged feeds (www.podlove.org/paged-feeds)
|
||||
|
||||
KEEP TRACK, SHARE & APPRECIATE<br>
|
||||
• Keep track of the best of the best by marking episodes as favourites<br>
|
||||
• Find that one episode through the playback history or by searching (titles and shownotes)<br>
|
||||
• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
|
||||
• Support content creators with Flattr integration including automatic flattring
|
||||
|
||||
CONTROL THE SYSTEM<br>
|
||||
• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
|
||||
• Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
|
||||
• Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
|
||||
• Adapt to your environment using the light and dark theme<br>
|
||||
• Back-up your subscriptions with the gPodder.net integration and OPML export
|
||||
|
||||
<b>Join the AntennaPod community!</b><br>
|
||||
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
|
||||
|
||||
GitHub is the place to go for feature requests, bug reports and code contributions:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod
|
||||
|
||||
Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
|
||||
https://groups.google.com/forum/#!forum/antennapod
|
||||
|
||||
Have a question or want to give us feedback?
|
||||
https://twitter.com/@AntennaPod
|
||||
|
||||
Transifex is the place to help with translations:<br>
|
||||
https://www.transifex.com/antennapod/antennapod
|
||||
|
||||
Check out our Beta Testing programme to get the latest features first:<br>
|
||||
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
|
|
@ -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:orientation="vertical">
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/timer_about_to_expire_label"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<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/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/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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_layout"
|
||||
android:layout_width="@dimen/drawer_width"
|
||||
|
@ -23,8 +21,6 @@
|
|||
android:id="@+id/imgvCover"
|
||||
android:layout_width="@dimen/thumbnail_length_navlist"
|
||||
android:layout_height="@dimen/thumbnail_length_navlist"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
|
||||
android:layout_marginTop="4dp"
|
||||
|
@ -34,8 +30,8 @@
|
|||
android:padding="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="?attr/ic_settings"
|
||||
tools:src="@android:drawable/sym_def_app_icon"
|
||||
tools:background="@android:color/holo_orange_dark" />
|
||||
tools:background="@android:color/holo_orange_dark"
|
||||
tools:src="@android:drawable/sym_def_app_icon"></ImageView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -47,16 +43,15 @@
|
|||
android:text="@string/settings_label"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/text_size_navdrawer"
|
||||
tools:background="@android:color/holo_green_light"/>
|
||||
|
||||
tools:background="@android:color/holo_green_light" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="@dimen/drawer_width"
|
||||
android:layout_height="1dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_above="@id/nav_settings"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="?android:attr/listDivider"
|
||||
tools:background="@android:color/holo_red_dark" />
|
||||
|
||||
|
@ -64,7 +59,6 @@
|
|||
android:id="@+id/nav_list"
|
||||
android:layout_width="@dimen/drawer_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_above="@id/divider"
|
||||
android:layout_alignParentTop="true"
|
||||
android:choiceMode="singleChoice"
|
||||
|
@ -74,7 +68,6 @@
|
|||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
tools:listitem="@layout/nav_listitem"
|
||||
tools:background="@android:color/holo_purple" />
|
||||
|
||||
tools:background="@android:color/holo_purple"
|
||||
tools:listitem="@layout/nav_listitem"></ListView>
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
@ -10,31 +10,28 @@
|
|||
android:id="@+id/imgvCover"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/ic_launcher" />
|
||||
tools:src="@drawable/ic_launcher">
|
||||
</de.danoeh.antennapod.view.SquareImageView>
|
||||
|
||||
<com.joanzapata.iconify.widget.IconTextView
|
||||
android:id="@+id/txtvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/light_gray"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:background="@color/light_gray"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<jp.shts.android.library.TriangleLabelView
|
||||
android:id="@+id/triangleCountView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_gravity="right|top"
|
||||
app:backgroundColor="@color/antennapod_blue"
|
||||
app:corner="rightTop"
|
||||
app:primaryText="Test"
|
||||
app:primaryTextColor="@color/white"
|
||||
app:primaryTextSize="12sp"
|
||||
android:layout_gravity="right|top"/>
|
||||
|
||||
app:primaryTextSize="12sp">
|
||||
</jp.shts.android.library.TriangleLabelView>
|
||||
</FrameLayout>
|
||||
|
|
|
@ -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"/>
|
||||
<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/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/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>
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
android:title="@string/queued_label"/>
|
||||
<item android:id="@+id/check_not_queued"
|
||||
android:title="@string/not_queued_label"/>
|
||||
<item android:id="@+id/check_has_media"
|
||||
android:title="@string/has_media"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
|
|
|
@ -65,6 +65,12 @@
|
|||
android:title="@string/share_feed_url_label">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/rename_item"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/rename_feed_label"
|
||||
custom:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/remove_item"
|
||||
android:icon="?attr/content_discard"
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
android:menuCategory="container"
|
||||
android:title="@string/mark_all_read_label" />
|
||||
|
||||
<item
|
||||
android:id="@+id/rename_item"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/rename_feed_label" />
|
||||
|
||||
<item
|
||||
android:id="@+id/remove_item"
|
||||
android:menuCategory="container"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<automotiveApp>
|
||||
<uses name="media"/>
|
||||
</automotiveApp>
|
|
@ -117,6 +117,12 @@
|
|||
android:key="prefPlaybackRewindDeltaLauncher"
|
||||
android:summary="@string/pref_rewind_sum"
|
||||
android:title="@string/pref_rewind" />
|
||||
<de.danoeh.antennapod.preferences.SwitchCompatPreference
|
||||
android:defaultValue="false"
|
||||
android:enabled="true"
|
||||
android:key="prefHardwarePreviousButtonRestarts"
|
||||
android:summary="@string/pref_hardwarePreviousButtonRestarts_sum"
|
||||
android:title="@string/pref_hardwarePreviousButtonRestarts_title"/>
|
||||
<de.danoeh.antennapod.preferences.SwitchCompatPreference
|
||||
android:defaultValue="true"
|
||||
android:enabled="true"
|
||||
|
@ -162,6 +168,12 @@
|
|||
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/network_pref">
|
||||
<de.danoeh.antennapod.preferences.SwitchCompatPreference
|
||||
android:defaultValue="true"
|
||||
android:enabled="true"
|
||||
android:key="prefEnqueueDownloaded"
|
||||
android:summary="@string/pref_enqueue_downloaded_summary"
|
||||
android:title="@string/pref_enqueue_downloaded_title" />
|
||||
<Preference
|
||||
android:key="prefAutoUpdateIntervall"
|
||||
android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
|
||||
|
@ -254,8 +266,12 @@
|
|||
android:summary="@string/pref_gpodnet_setlogin_information_sum"/>
|
||||
<Preference
|
||||
android:key="pref_gpodnet_sync"
|
||||
android:title="@string/pref_gpodnet_sync_title"
|
||||
android:summary="@string/pref_gpodnet_sync_sum"/>
|
||||
android:title="@string/pref_gpodnet_sync_changes_title"
|
||||
android:summary="@string/pref_gpodnet_sync_changes_sum"/>
|
||||
<Preference
|
||||
android:key="pref_gpodnet_force_full_sync"
|
||||
android:title="@string/pref_gpodnet_full_sync_title"
|
||||
android:summary="@string/pref_gpodnet_full_sync_sum"/>
|
||||
<Preference
|
||||
android:key="pref_gpodnet_logout"
|
||||
android:title="@string/pref_gpodnet_logout_title"/>
|
||||
|
@ -285,6 +301,9 @@
|
|||
<Preference
|
||||
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"/>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
<p>Created by Daniel Oeh</p>
|
||||
|
||||
<p>Copyright © 2015 AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a></p>
|
||||
<p>Copyright © 2012-@year@ AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a></p>
|
||||
|
||||
<p>Licensed under the MIT License <a href="LICENSE.txt">(View)</a></p>
|
||||
</div>
|
||||
|
|
14
build.gradle
14
build.gradle
|
@ -6,10 +6,10 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
classpath "me.tatarka:gradle-retrolambda:3.2.4"
|
||||
classpath "com.android.tools.build:gradle:2.2.3"
|
||||
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"
|
||||
}
|
||||
|
@ -51,9 +51,9 @@ project.ext {
|
|||
glideOkhttpIntegrationVersion = "1.4.0"
|
||||
iconifyVersion = "2.2.2"
|
||||
jsoupVersion = "1.9.2"
|
||||
materialDialogsVersion = "0.8.5.6@aar"
|
||||
okhttpVersion = "2.7.5"
|
||||
okioVersion = "1.9.0"
|
||||
materialDialogsVersion = "0.8.5.8@aar"
|
||||
okhttpVersion = "3.4.2"
|
||||
okioVersion = "1.11.0"
|
||||
recyclerviewFlexibledividerVersion = "1.2.6"
|
||||
robotiumSoloVersion = "5.6.0"
|
||||
rxAndroidVersion = "1.2.1"
|
||||
|
@ -68,7 +68,7 @@ project.ext {
|
|||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = "2.14.1"
|
||||
gradleVersion = "3.2.1"
|
||||
}
|
||||
|
||||
// free build hack: common functions
|
||||
|
|
|
@ -2,6 +2,8 @@ general:
|
|||
artifacts:
|
||||
- app/build/outputs/apk
|
||||
machine:
|
||||
environment:
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
java:
|
||||
version: oraclejdk8
|
||||
dependencies:
|
||||
|
@ -16,4 +18,5 @@ dependencies:
|
|||
|
||||
test:
|
||||
override:
|
||||
- ./gradlew assembleDebug -PdisablePreDex
|
||||
- ./gradlew assembleDebug -PdisablePreDex:
|
||||
timeout: 1800
|
||||
|
|
|
@ -56,9 +56,9 @@ dependencies {
|
|||
compile "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
|
||||
compile "org.jsoup:jsoup:$jsoupVersion"
|
||||
compile "com.github.bumptech.glide:glide:$glideVersion"
|
||||
compile "com.github.bumptech.glide:okhttp-integration:$glideOkhttpIntegrationVersion"
|
||||
compile "com.squareup.okhttp:okhttp:$okhttpVersion"
|
||||
compile "com.squareup.okhttp:okhttp-urlconnection:$okhttpVersion"
|
||||
compile "com.github.bumptech.glide:okhttp3-integration:$glideOkhttpIntegrationVersion@aar"
|
||||
compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
|
||||
compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
|
||||
compile "com.squareup.okio:okio:$okioVersion"
|
||||
compile "com.nineoldandroids:library:2.4.0"
|
||||
compile "de.greenrobot:eventbus:$eventbusVersion"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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";
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
|
@ -1,4 +1,4 @@
|
|||
package de.danoeh.antennapod.core.opml;
|
||||
package de.danoeh.antennapod.core.export.opml;
|
||||
|
||||
import android.util.Log;
|
||||
|
|
@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,11 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
public static final String TYPE_RSS091 = "rss";
|
||||
public static final String TYPE_ATOM1 = "atom";
|
||||
|
||||
private String title;
|
||||
/* title as defined by the feed */
|
||||
private String feedTitle;
|
||||
/* custom title set by the user */
|
||||
private String customTitle;
|
||||
|
||||
/**
|
||||
* Contains 'id'-element in Atom feed.
|
||||
*/
|
||||
|
@ -92,13 +96,14 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
/**
|
||||
* This constructor is used for restoring a feed from the database.
|
||||
*/
|
||||
public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
|
||||
public Feed(long id, String lastUpdate, String title, String customTitle, String link, String description, String paymentLink,
|
||||
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
|
||||
String downloadUrl, boolean downloaded, FlattrStatus status, boolean paged, String nextPageLink,
|
||||
String filter, boolean lastUpdateFailed) {
|
||||
super(fileUrl, downloadUrl, downloaded);
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.feedTitle = title;
|
||||
this.customTitle = customTitle;
|
||||
this.lastUpdate = lastUpdate;
|
||||
this.link = link;
|
||||
this.description = description;
|
||||
|
@ -126,7 +131,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
|
||||
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
|
||||
String downloadUrl, boolean downloaded) {
|
||||
this(id, lastUpdate, title, link, description, paymentLink, author, language, type, feedIdentifier, image,
|
||||
this(id, lastUpdate, title, null, link, description, paymentLink, author, language, type, feedIdentifier, image,
|
||||
fileUrl, downloadUrl, downloaded, new FlattrStatus(), false, null, null, false);
|
||||
}
|
||||
|
||||
|
@ -154,7 +159,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
*/
|
||||
public Feed(String url, String lastUpdate, String title) {
|
||||
this(url, lastUpdate);
|
||||
this.title = title;
|
||||
this.feedTitle = title;
|
||||
this.flattrStatus = new FlattrStatus();
|
||||
}
|
||||
|
||||
|
@ -171,6 +176,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
|
||||
int indexLastUpdate = cursor.getColumnIndex(PodDBAdapter.KEY_LASTUPDATE);
|
||||
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
|
||||
int indexCustomTitle = cursor.getColumnIndex(PodDBAdapter.KEY_CUSTOM_TITLE);
|
||||
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
|
||||
int indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION);
|
||||
int indexPaymentLink = cursor.getColumnIndex(PodDBAdapter.KEY_PAYMENT_LINK);
|
||||
|
@ -191,6 +197,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
cursor.getLong(indexId),
|
||||
cursor.getString(indexLastUpdate),
|
||||
cursor.getString(indexTitle),
|
||||
cursor.getString(indexCustomTitle),
|
||||
cursor.getString(indexLink),
|
||||
cursor.getString(indexDescription),
|
||||
cursor.getString(indexPaymentLink),
|
||||
|
@ -268,8 +275,8 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
return feedIdentifier;
|
||||
} else if (download_url != null && !download_url.isEmpty()) {
|
||||
return download_url;
|
||||
} else if (title != null && !title.isEmpty()) {
|
||||
return title;
|
||||
} else if (feedTitle != null && !feedTitle.isEmpty()) {
|
||||
return feedTitle;
|
||||
} else {
|
||||
return link;
|
||||
}
|
||||
|
@ -277,8 +284,8 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
|
||||
@Override
|
||||
public String getHumanReadableIdentifier() {
|
||||
if (title != null) {
|
||||
return title;
|
||||
if (feedTitle != null) {
|
||||
return feedTitle;
|
||||
} else {
|
||||
return download_url;
|
||||
}
|
||||
|
@ -287,8 +294,8 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
public void updateFromOther(Feed other) {
|
||||
// don't update feed's download_url, we do that manually if redirected
|
||||
// see AntennapodHttpClient
|
||||
if (other.title != null) {
|
||||
title = other.title;
|
||||
if (other.feedTitle != null) {
|
||||
feedTitle = other.feedTitle;
|
||||
}
|
||||
if (other.feedIdentifier != null) {
|
||||
feedIdentifier = other.feedIdentifier;
|
||||
|
@ -323,7 +330,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
if (super.compareWithOther(other)) {
|
||||
return true;
|
||||
}
|
||||
if (!TextUtils.equals(title, other.title)) {
|
||||
if (!TextUtils.equals(feedTitle, other.feedTitle)) {
|
||||
return true;
|
||||
}
|
||||
if (other.feedIdentifier != null) {
|
||||
|
@ -384,11 +391,28 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
|
|||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
return !TextUtils.isEmpty(customTitle) ? customTitle : feedTitle;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
this.feedTitle = title;
|
||||
}
|
||||
|
||||
public String getFeedTitle() {
|
||||
return this.feedTitle;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCustomTitle() {
|
||||
return this.customTitle;
|
||||
}
|
||||
|
||||
public void setCustomTitle(String customTitle) {
|
||||
if(customTitle == null || customTitle.equals(feedTitle)) {
|
||||
this.customTitle = null;
|
||||
} else {
|
||||
this.customTitle = customTitle;
|
||||
}
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.danoeh.antennapod.core.feed;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
@ -269,6 +270,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FeedMedia getMedia() {
|
||||
return media;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ public class FeedItemFilter {
|
|||
private boolean showNotQueued = false;
|
||||
private boolean showDownloaded = false;
|
||||
private boolean showNotDownloaded = false;
|
||||
private boolean showHasMedia = false;
|
||||
|
||||
public FeedItemFilter(String properties) {
|
||||
this(TextUtils.split(properties, ","));
|
||||
|
@ -49,6 +50,9 @@ public class FeedItemFilter {
|
|||
case "not_downloaded":
|
||||
showNotDownloaded = true;
|
||||
break;
|
||||
case "has_media":
|
||||
showHasMedia = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +86,8 @@ public class FeedItemFilter {
|
|||
if (showDownloaded && !downloaded) continue;
|
||||
if (showNotDownloaded && downloaded) continue;
|
||||
|
||||
if (showHasMedia && !item.hasMedia()) continue;
|
||||
|
||||
// If the item reaches here, it meets all criteria
|
||||
result.add(item);
|
||||
}
|
||||
|
|
|
@ -4,15 +4,12 @@ import android.content.Context;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher;
|
||||
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher;
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||
import com.bumptech.glide.load.model.GlideUrl;
|
||||
import com.bumptech.glide.load.model.ModelLoader;
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.squareup.okhttp.Interceptor;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -22,9 +19,13 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
|||
import de.danoeh.antennapod.core.service.download.HttpDownloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* @see com.bumptech.glide.integration.okhttp.OkHttpUrlLoader
|
||||
* @see com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
|
||||
*/
|
||||
public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
|
||||
|
||||
|
@ -42,9 +43,10 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
|
|||
if (internalClient == null) {
|
||||
synchronized (Factory.class) {
|
||||
if (internalClient == null) {
|
||||
internalClient = AntennapodHttpClient.newHttpClient();
|
||||
internalClient.interceptors().add(new NetworkAllowanceInterceptor());
|
||||
internalClient.interceptors().add(new BasicAuthenticationInterceptor());
|
||||
OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder();
|
||||
builder.interceptors().add(new NetworkAllowanceInterceptor());
|
||||
builder.interceptors().add(new BasicAuthenticationInterceptor());
|
||||
internalClient = builder.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,8 +115,8 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
|
|||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
com.squareup.okhttp.Request request = chain.request();
|
||||
String url = request.urlString();
|
||||
Request request = chain.request();
|
||||
String url = request.url().toString();
|
||||
String authentication = DBReader.getImageAuthentication(url);
|
||||
|
||||
if(TextUtils.isEmpty(authentication)) {
|
||||
|
@ -125,7 +127,7 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
|
|||
// add authentication
|
||||
String[] auth = authentication.split(":");
|
||||
String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1");
|
||||
com.squareup.okhttp.Request newRequest = request
|
||||
Request newRequest = request
|
||||
.newBuilder()
|
||||
.addHeader("Authorization", credentials)
|
||||
.build();
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
package de.danoeh.antennapod.core.gpoddernet;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.squareup.okhttp.Credentials;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -38,6 +29,13 @@ import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag;
|
|||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetUploadChangesResponse;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* Communicates with the gpodder.net service.
|
||||
|
@ -570,15 +568,7 @@ public class GpodnetService {
|
|||
e.printStackTrace();
|
||||
throw new GpodnetServiceException(e);
|
||||
} finally {
|
||||
if (response != null && body != null) {
|
||||
try {
|
||||
body.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new GpodnetServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
body.close();
|
||||
}
|
||||
return responseString;
|
||||
}
|
||||
|
@ -605,12 +595,7 @@ public class GpodnetService {
|
|||
throw new GpodnetServiceException(e);
|
||||
} finally {
|
||||
if (body != null) {
|
||||
try {
|
||||
body.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new GpodnetServiceException(e);
|
||||
}
|
||||
body.close();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -619,12 +604,7 @@ public class GpodnetService {
|
|||
private String getStringFromResponseBody(@NonNull ResponseBody body)
|
||||
throws GpodnetServiceException {
|
||||
ByteArrayOutputStream outputStream;
|
||||
int contentLength = 0;
|
||||
try {
|
||||
contentLength = (int) body.contentLength();
|
||||
} catch (IOException ignore) {
|
||||
// ignore
|
||||
}
|
||||
int contentLength = (int) body.contentLength();
|
||||
if (contentLength > 0) {
|
||||
outputStream = new ByteArrayOutputStream(contentLength);
|
||||
} else {
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
|
||||
|
@ -65,6 +64,7 @@ public class UserPreferences {
|
|||
public static final String PREF_UNPAUSE_ON_HEADSET_RECONNECT = "prefUnpauseOnHeadsetReconnect";
|
||||
public static final String PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT = "prefUnpauseOnBluetoothReconnect";
|
||||
public static final String PREF_HARDWARE_FOWARD_BUTTON_SKIPS = "prefHardwareForwardButtonSkips";
|
||||
public static final String PREF_HARDWARE_PREVIOUS_BUTTON_RESTARTS = "prefHardwarePreviousButtonRestarts";
|
||||
public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
|
||||
public static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode";
|
||||
public static final String PREF_AUTO_DELETE = "prefAutoDelete";
|
||||
|
@ -74,6 +74,7 @@ public class UserPreferences {
|
|||
public static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
|
||||
|
||||
// Network
|
||||
public static final String PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded";
|
||||
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
|
||||
public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
|
||||
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
|
||||
|
@ -122,13 +123,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;
|
||||
|
@ -257,11 +259,10 @@ public class UserPreferences {
|
|||
return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if new queue elements are added to the front
|
||||
*
|
||||
* @return {@code true} if new queue elements are added to the front; {@code false} otherwise
|
||||
*/
|
||||
public static boolean enqueueDownloadedEpisodes() {
|
||||
return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true);
|
||||
}
|
||||
|
||||
public static boolean enqueueAtFront() {
|
||||
return prefs.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
|
||||
}
|
||||
|
@ -282,6 +283,10 @@ public class UserPreferences {
|
|||
return prefs.getBoolean(PREF_HARDWARE_FOWARD_BUTTON_SKIPS, false);
|
||||
}
|
||||
|
||||
public static boolean shouldHardwarePreviousButtonRestart() {
|
||||
return prefs.getBoolean(PREF_HARDWARE_PREVIOUS_BUTTON_RESTARTS, false);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isFollowQueue() {
|
||||
return prefs.getBoolean(PREF_FOLLOW_QUEUE, true);
|
||||
|
@ -332,7 +337,10 @@ public class UserPreferences {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Returns update interval in milliseconds; value 0 means that auto update is disabled
|
||||
* or feeds are updated at a certain time of day
|
||||
*/
|
||||
public static long getUpdateInterval() {
|
||||
String updateInterval = prefs.getString(PREF_UPDATE_INTERVAL, "0");
|
||||
if(!updateInterval.contains(":")) {
|
||||
|
@ -754,12 +762,12 @@ public class UserPreferences {
|
|||
if (timeOfDay.length == 2) {
|
||||
restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
|
||||
} else {
|
||||
long hours = getUpdateInterval();
|
||||
long startTrigger = hours;
|
||||
long milliseconds = getUpdateInterval();
|
||||
long startTrigger = milliseconds;
|
||||
if (now) {
|
||||
startTrigger = TimeUnit.SECONDS.toMillis(10);
|
||||
}
|
||||
restartUpdateIntervalAlarm(startTrigger, hours);
|
||||
restartUpdateIntervalAlarm(startTrigger, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,6 @@ import android.support.annotation.NonNull;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.Credentials;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.http.StatusLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
|
@ -20,16 +14,27 @@ import java.net.InetSocketAddress;
|
|||
import java.net.Proxy;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.JavaNetCookieJar;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.http.StatusLine;
|
||||
|
||||
/**
|
||||
* Provides access to a HttpClient singleton.
|
||||
|
@ -50,13 +55,13 @@ public class AntennapodHttpClient {
|
|||
*/
|
||||
public static synchronized OkHttpClient getHttpClient() {
|
||||
if (httpClient == null) {
|
||||
httpClient = newHttpClient();
|
||||
httpClient = newBuilder().build();
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public static synchronized void reinit() {
|
||||
httpClient = newHttpClient();
|
||||
httpClient = newBuilder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,33 +72,33 @@ public class AntennapodHttpClient {
|
|||
* @return http client
|
||||
*/
|
||||
@NonNull
|
||||
public static OkHttpClient newHttpClient() {
|
||||
public static OkHttpClient.Builder newBuilder() {
|
||||
Log.d(TAG, "Creating new instance of HTTP client");
|
||||
|
||||
System.setProperty("http.maxConnections", String.valueOf(MAX_CONNECTIONS));
|
||||
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
|
||||
// detect 301 Moved permanently and 308 Permanent Redirect
|
||||
client.networkInterceptors().add(chain -> {
|
||||
builder.networkInterceptors().add(chain -> {
|
||||
Request request = chain.request();
|
||||
Response response = chain.proceed(request);
|
||||
if (response.code() == HttpURLConnection.HTTP_MOVED_PERM ||
|
||||
response.code() == StatusLine.HTTP_PERM_REDIRECT) {
|
||||
String location = response.header("Location");
|
||||
if (location.startsWith("/")) { // URL is not absolute, but relative
|
||||
URL url = request.url();
|
||||
location = url.getProtocol() + "://" + url.getHost() + location;
|
||||
HttpUrl url = request.url();
|
||||
location = url.scheme() + "://" + url.host() + location;
|
||||
} else if (!location.toLowerCase().startsWith("http://") &&
|
||||
!location.toLowerCase().startsWith("https://")) {
|
||||
// Reference is relative to current path
|
||||
URL url = request.url();
|
||||
String path = url.getPath();
|
||||
HttpUrl url = request.url();
|
||||
String path = url.encodedPath();
|
||||
String newPath = path.substring(0, path.lastIndexOf("/") + 1) + location;
|
||||
location = url.getProtocol() + "://" + url.getHost() + newPath;
|
||||
location = url.scheme() + "://" + url.host() + newPath;
|
||||
}
|
||||
try {
|
||||
DBWriter.updateFeedDownloadURL(request.urlString(), location).get();
|
||||
DBWriter.updateFeedDownloadURL(request.url().toString(), location).get();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
|
@ -104,26 +109,26 @@ public class AntennapodHttpClient {
|
|||
// set cookie handler
|
||||
CookieManager cm = new CookieManager();
|
||||
cm.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||
client.setCookieHandler(cm);
|
||||
builder.cookieJar(new JavaNetCookieJar(cm));
|
||||
|
||||
// set timeouts
|
||||
client.setConnectTimeout(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
client.setReadTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
client.setWriteTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
builder.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
builder.readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
builder.writeTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
|
||||
// configure redirects
|
||||
client.setFollowRedirects(true);
|
||||
client.setFollowSslRedirects(true);
|
||||
builder.followRedirects(true);
|
||||
builder.followSslRedirects(true);
|
||||
|
||||
ProxyConfig config = UserPreferences.getProxyConfig();
|
||||
if (config.type != Proxy.Type.DIRECT) {
|
||||
int port = config.port > 0 ? config.port : ProxyConfig.DEFAULT_PORT;
|
||||
SocketAddress address = InetSocketAddress.createUnresolved(config.host, port);
|
||||
Proxy proxy = new Proxy(config.type, address);
|
||||
client.setProxy(proxy);
|
||||
builder.proxy(proxy);
|
||||
if (!TextUtils.isEmpty(config.username)) {
|
||||
String credentials = Credentials.basic(config.username, config.password);
|
||||
client.interceptors().add(chain -> {
|
||||
builder.interceptors().add(chain -> {
|
||||
Request request = chain.request().newBuilder()
|
||||
.header("Proxy-Authorization", credentials).build();
|
||||
return chain.proceed(request);
|
||||
|
@ -131,9 +136,9 @@ public class AntennapodHttpClient {
|
|||
}
|
||||
}
|
||||
if(16 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT < 21) {
|
||||
client.setSslSocketFactory(new CustomSslSocketFactory());
|
||||
builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager());
|
||||
}
|
||||
return client;
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,6 +151,23 @@ public class AntennapodHttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
private static X509TrustManager trustManager() {
|
||||
try {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
|
||||
TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
||||
throw new IllegalStateException("Unexpected default trust managers:"
|
||||
+ Arrays.toString(trustManagers));
|
||||
}
|
||||
return (X509TrustManager) trustManagers[0];
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomSslSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory factory;
|
||||
|
|
|
@ -842,11 +842,14 @@ public class DownloadService extends Service {
|
|||
successful = false;
|
||||
reason = DownloadError.ERROR_PARSER_EXCEPTION;
|
||||
reasonDetailed = e.getMessage();
|
||||
} finally {
|
||||
File feedFile = new File(request.getDestination());
|
||||
if(feedFile.exists()) {
|
||||
boolean deleted = feedFile.delete();
|
||||
Log.d(TAG, "Deletion of file '" + feedFile.getAbsolutePath() + "' " + (deleted ? "successful" : "FAILED"));
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup();
|
||||
|
||||
|
||||
if (successful) {
|
||||
// we create a 'successful' download log if the feed's last refresh failed
|
||||
List<DownloadStatus> log = DBReader.getFeedDownloadLog(feed);
|
||||
|
@ -1048,7 +1051,8 @@ public class DownloadService extends Service {
|
|||
|
||||
DBWriter.setFeedMedia(media).get();
|
||||
|
||||
if (item != null && !DBTasks.isInQueue(DownloadService.this, item.getId())) {
|
||||
if (item != null && UserPreferences.enqueueDownloadedEpisodes() &&
|
||||
!DBTasks.isInQueue(DownloadService.this, item.getId())) {
|
||||
DBWriter.addQueueItem(DownloadService.this, item).get();
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
|
|
|
@ -2,21 +2,7 @@ package de.danoeh.antennapod.core.service.download;
|
|||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.squareup.okhttp.Interceptor;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Protocol;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||
import de.danoeh.antennapod.core.util.URIUtil;
|
||||
import okio.ByteString;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
|
@ -32,6 +18,22 @@ import java.net.UnknownHostException;
|
|||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||
import de.danoeh.antennapod.core.util.URIUtil;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okio.ByteString;
|
||||
|
||||
public class HttpDownloader extends Downloader {
|
||||
private static final String TAG = "HttpDownloader";
|
||||
|
||||
|
@ -57,8 +59,9 @@ public class HttpDownloader extends Downloader {
|
|||
}
|
||||
}
|
||||
|
||||
OkHttpClient httpClient = AntennapodHttpClient.newHttpClient();
|
||||
httpClient.interceptors().add(new BasicAuthorizationInterceptor(request));
|
||||
OkHttpClient.Builder httpClientBuilder = AntennapodHttpClient.newBuilder();
|
||||
httpClientBuilder.interceptors().add(new BasicAuthorizationInterceptor(request));
|
||||
OkHttpClient httpClient = httpClientBuilder.build();
|
||||
RandomAccessFile out = null;
|
||||
InputStream connection;
|
||||
ResponseBody responseBody = null;
|
||||
|
@ -103,7 +106,9 @@ public class HttpDownloader extends Downloader {
|
|||
} catch (IOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
if (e.getMessage().contains("PROTOCOL_ERROR")) {
|
||||
httpClient.setProtocols(Collections.singletonList(Protocol.HTTP_1_1));
|
||||
httpClient = httpClient.newBuilder()
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.build();
|
||||
response = httpClient.newCall(httpReq.build()).execute();
|
||||
} else {
|
||||
throw e;
|
||||
|
|
|
@ -146,7 +146,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
|
||||
if (!media.getIdentifier().equals(playable.getIdentifier())) {
|
||||
final Playable oldMedia = media;
|
||||
executor.submit(() -> callback.onPostPlayback(oldMedia, false, true));
|
||||
executor.submit(() -> callback.onPostPlayback(oldMedia, false, false, true));
|
||||
}
|
||||
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
|
||||
|
@ -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) {
|
||||
|
@ -755,7 +758,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
|
||||
|
||||
@Override
|
||||
protected Future<?> endPlayback(final boolean wasSkipped, final boolean shouldContinue, final boolean toStoppedState) {
|
||||
protected Future<?> endPlayback(final boolean hasEnded, final boolean wasSkipped,
|
||||
final boolean shouldContinue, final boolean toStoppedState) {
|
||||
return executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
|
@ -813,7 +817,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
final boolean hasNext = nextMedia != null;
|
||||
|
||||
executor.submit(() -> callback.onPostPlayback(currentMedia, !wasSkipped, hasNext));
|
||||
executor.submit(() -> callback.onPostPlayback(currentMedia, hasEnded, wasSkipped, hasNext));
|
||||
} else if (isPlaying) {
|
||||
callback.onPlaybackPause(currentMedia, currentMedia.getPosition());
|
||||
}
|
||||
|
@ -848,27 +852,24 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
|
||||
private IPlayer setMediaPlayerListeners(IPlayer mp) {
|
||||
if (mp != null && media != null) {
|
||||
if (media.getMediaType() == MediaType.AUDIO) {
|
||||
((AudioPlayer) mp)
|
||||
.setOnCompletionListener(audioCompletionListener);
|
||||
((AudioPlayer) mp)
|
||||
.setOnSeekCompleteListener(audioSeekCompleteListener);
|
||||
((AudioPlayer) mp).setOnErrorListener(audioErrorListener);
|
||||
((AudioPlayer) mp)
|
||||
.setOnBufferingUpdateListener(audioBufferingUpdateListener);
|
||||
((AudioPlayer) mp).setOnInfoListener(audioInfoListener);
|
||||
((AudioPlayer) mp).setOnSpeedAdjustmentAvailableChangedListener(audioSetSpeedAbilityListener);
|
||||
} else {
|
||||
((VideoPlayer) mp)
|
||||
.setOnCompletionListener(videoCompletionListener);
|
||||
((VideoPlayer) mp)
|
||||
.setOnSeekCompleteListener(videoSeekCompleteListener);
|
||||
((VideoPlayer) mp).setOnErrorListener(videoErrorListener);
|
||||
((VideoPlayer) mp)
|
||||
.setOnBufferingUpdateListener(videoBufferingUpdateListener);
|
||||
((VideoPlayer) mp).setOnInfoListener(videoInfoListener);
|
||||
}
|
||||
if (mp == null || media == null) {
|
||||
return mp;
|
||||
}
|
||||
if (media.getMediaType() == MediaType.VIDEO) {
|
||||
VideoPlayer vp = (VideoPlayer) mp;
|
||||
vp.setOnCompletionListener(videoCompletionListener);
|
||||
vp.setOnSeekCompleteListener(videoSeekCompleteListener);
|
||||
vp.setOnErrorListener(videoErrorListener);
|
||||
vp.setOnBufferingUpdateListener(videoBufferingUpdateListener);
|
||||
vp.setOnInfoListener(videoInfoListener);
|
||||
} else {
|
||||
AudioPlayer ap = (AudioPlayer) mp;
|
||||
ap.setOnCompletionListener(audioCompletionListener);
|
||||
ap.setOnSeekCompleteListener(audioSeekCompleteListener);
|
||||
ap.setOnErrorListener(audioErrorListener);
|
||||
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
|
||||
ap.setOnInfoListener(audioInfoListener);
|
||||
ap.setOnSpeedAdjustmentAvailableChangedListener(audioSetSpeedAbilityListener);
|
||||
}
|
||||
return mp;
|
||||
}
|
||||
|
@ -880,7 +881,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
mp -> genericOnCompletion();
|
||||
|
||||
private void genericOnCompletion() {
|
||||
endPlayback(false, true, true);
|
||||
endPlayback(true, false, true, true);
|
||||
}
|
||||
|
||||
private final MediaPlayer.OnBufferingUpdateListener audioBufferingUpdateListener =
|
||||
|
|
|
@ -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
|
||||
|
@ -298,7 +301,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
List<MediaSessionCompat.QueueItem> queueItems = new ArrayList<>();
|
||||
try {
|
||||
for (FeedItem feedItem : taskManager.getQueue()) {
|
||||
queueItems.add(new MediaSessionCompat.QueueItem(feedItem.getMedia().getMediaItem().getDescription(), feedItem.getId()));
|
||||
if(feedItem.getMedia() != null) {
|
||||
MediaDescriptionCompat mediaDescription = feedItem.getMedia().getMediaItem().getDescription();
|
||||
queueItems.add(new MediaSessionCompat.QueueItem(mediaDescription, feedItem.getId()));
|
||||
}
|
||||
}
|
||||
mediaSession.setQueue(queueItems);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -478,6 +484,14 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
mediaPlayer.seekDelta(UserPreferences.getFastForwardSecs() * 1000);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
if(UserPreferences.shouldHardwarePreviousButtonRestart()) {
|
||||
// user wants to restart current episode
|
||||
mediaPlayer.seekTo(0);
|
||||
} else {
|
||||
// user wants to rewind current episode
|
||||
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
||||
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
|
||||
break;
|
||||
|
@ -597,6 +611,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
writePlayerStatusPlaybackPreferences();
|
||||
setupNotification(newInfo);
|
||||
started = true;
|
||||
// set sleep timer if auto-enabled
|
||||
if(newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
|
||||
SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
|
||||
setSleepTimer(SleepTimerPreferences.timerMillis(), SleepTimerPreferences.shakeToReset(),
|
||||
SleepTimerPreferences.vibrate());
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
|
@ -669,8 +689,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean playingNext) {
|
||||
PlaybackService.this.onPostPlayback(media, ended, playingNext);
|
||||
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped,
|
||||
boolean playingNext) {
|
||||
PlaybackService.this.onPostPlayback(media, ended, skipped, playingNext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -768,16 +789,20 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
* Even though these tasks aren't supposed to be resource intensive, a good practice is to
|
||||
* usually call this method on a background thread.
|
||||
*
|
||||
* @param playable the media object that was playing. It is assumed that its position property
|
||||
* was updated before this method was called.
|
||||
* @param ended if true, it signals that {@param playable} was played until its end.
|
||||
* In such case, the position property of the media becomes irrelevant for most of
|
||||
* the tasks (although it's still a good practice to keep it accurate).
|
||||
* @param playingNext if true, it means another media object is being loaded in place of this one.
|
||||
* @param playable the media object that was playing. It is assumed that its position
|
||||
* property was updated before this method was called.
|
||||
* @param ended if true, it signals that {@param playable} was played until its end.
|
||||
* In such case, the position property of the media becomes irrelevant for
|
||||
* most of the tasks (although it's still a good practice to keep it
|
||||
* accurate).
|
||||
* @param skipped if the user pressed a skip >| button.
|
||||
* @param playingNext if true, it means another media object is being loaded in place of this
|
||||
* one.
|
||||
* Instances when we'd set it to false would be when we're not following the
|
||||
* queue or when the queue has ended.
|
||||
*/
|
||||
private void onPostPlayback(final Playable playable, boolean ended, boolean playingNext) {
|
||||
private void onPostPlayback(final Playable playable, boolean ended, boolean skipped,
|
||||
boolean playingNext) {
|
||||
if (playable == null) {
|
||||
Log.e(TAG, "Cannot do post-playback processing: media was null");
|
||||
return;
|
||||
|
@ -808,7 +833,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
|
||||
if (item != null) {
|
||||
if (ended || smartMarkAsPlayed ||
|
||||
!UserPreferences.shouldSkipKeepEpisode()) {
|
||||
(skipped && !UserPreferences.shouldSkipKeepEpisode())) {
|
||||
// only mark the item as played if we're not keeping it anyways
|
||||
DBWriter.markItemPlayed(item, FeedItem.PLAYED, ended);
|
||||
try {
|
||||
|
@ -829,7 +854,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
}
|
||||
}
|
||||
|
||||
if (ended || playingNext) {
|
||||
if (ended || skipped || playingNext) {
|
||||
DBWriter.addItemToPlaybackHistory(media);
|
||||
}
|
||||
}
|
||||
|
@ -838,11 +863,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() {
|
||||
|
@ -996,13 +1024,37 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
state = PlaybackStateCompat.STATE_NONE;
|
||||
}
|
||||
sessionState.setState(state, mediaPlayer.getPosition(), mediaPlayer.getPlaybackSpeed());
|
||||
sessionState.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
long capabilities = PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
| PlaybackStateCompat.ACTION_REWIND
|
||||
| PlaybackStateCompat.ACTION_FAST_FORWARD
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT);
|
||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
|
||||
|
||||
if (useSkipToPreviousForRewindInLockscreen()) {
|
||||
// Workaround to fool Android so that Lockscreen will expose a skip-to-previous button,
|
||||
// which will be used for rewind.
|
||||
// The workaround is used for pre Lollipop (Androidv5) devices.
|
||||
// For Androidv5+, lockscreen widges are really notifications (compact),
|
||||
// with an independent codepath
|
||||
//
|
||||
// @see #sessionCallback in the backing callback, skipToPrevious implementation
|
||||
// is actually the same as rewind. So no new inconsistency is created.
|
||||
// @see #setupNotification() for the method to create Androidv5+ lockscreen UI
|
||||
// with notification (compact)
|
||||
capabilities = capabilities | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||
}
|
||||
|
||||
sessionState.setActions(capabilities);
|
||||
mediaSession.setPlaybackState(sessionState.build());
|
||||
}
|
||||
|
||||
private static boolean useSkipToPreviousForRewindInLockscreen() {
|
||||
// showRewindOnCompactNotification() corresponds to the "Set Lockscreen Buttons"
|
||||
// Settings in UI.
|
||||
// Hence, from user perspective, he/she is setting the buttons for Lockscreen
|
||||
return ( UserPreferences.showRewindOnCompactNotification() &&
|
||||
(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by updateMediaSessionMetadata to load notification data in another thread.
|
||||
*/
|
||||
|
@ -1050,7 +1102,13 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
|
||||
PlaybackService.getPlayerActivityIntent(this),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
mediaSession.setMetadata(builder.build());
|
||||
try {
|
||||
mediaSession.setMetadata(builder.build());
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG, "Setting media session metadata", e);
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, null);
|
||||
mediaSession.setMetadata(builder.build());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1285,7 +1343,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
|
||||
if (info.playable != null) {
|
||||
Intent i = new Intent(whatChanged);
|
||||
i.putExtra("id", 1);
|
||||
i.putExtra("id", 1L);
|
||||
i.putExtra("artist", "");
|
||||
i.putExtra("album", info.playable.getFeedTitle());
|
||||
i.putExtra("track", info.playable.getEpisodeTitle());
|
||||
|
@ -1294,8 +1352,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
if (queue != null) {
|
||||
i.putExtra("ListSize", queue.size());
|
||||
}
|
||||
i.putExtra("duration", info.playable.getDuration());
|
||||
i.putExtra("position", info.playable.getPosition());
|
||||
i.putExtra("duration", (long) info.playable.getDuration());
|
||||
i.putExtra("position", (long) info.playable.getPosition());
|
||||
sendBroadcast(i);
|
||||
}
|
||||
}
|
||||
|
@ -1375,7 +1433,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
* Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true.
|
||||
*/
|
||||
private void pauseIfPauseOnDisconnect() {
|
||||
if (UserPreferences.isPauseOnHeadsetDisconnect()) {
|
||||
if (UserPreferences.isPauseOnHeadsetDisconnect() && !isCasting()) {
|
||||
if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
|
||||
transientPause = true;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,10 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
/**
|
||||
* Return value of some PSMP methods if the method call failed.
|
||||
*/
|
||||
public static final int INVALID_TIME = -1;
|
||||
static final int INVALID_TIME = -1;
|
||||
|
||||
protected volatile PlayerStatus playerStatus;
|
||||
volatile PlayerStatus oldPlayerStatus;
|
||||
volatile PlayerStatus playerStatus;
|
||||
|
||||
/**
|
||||
* A wifi-lock that is acquired if the media file is being streamed.
|
||||
|
@ -41,8 +42,8 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
protected final PSMPCallback callback;
|
||||
protected final Context context;
|
||||
|
||||
public PlaybackServiceMediaPlayer(@NonNull Context context,
|
||||
@NonNull PSMPCallback callback){
|
||||
PlaybackServiceMediaPlayer(@NonNull Context context,
|
||||
@NonNull PSMPCallback callback){
|
||||
this.context = context;
|
||||
this.callback = callback;
|
||||
|
||||
|
@ -204,7 +205,7 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
* @return The PSMPInfo object.
|
||||
*/
|
||||
public final synchronized PSMPInfo getPSMPInfo() {
|
||||
return new PSMPInfo(playerStatus, getPlayable());
|
||||
return new PSMPInfo(oldPlayerStatus, playerStatus, getPlayable());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,32 +229,35 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
protected abstract void setPlayable(Playable playable);
|
||||
|
||||
public void skip() {
|
||||
endPlayback(true, true, true);
|
||||
endPlayback(false, true, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends playback of current media (if any) and moves into INDETERMINATE state, unless
|
||||
* {@param toStoppedState} is set to true, in which case it moves into STOPPED state.
|
||||
*
|
||||
* @see #endPlayback(boolean, boolean, boolean)
|
||||
* @see #endPlayback(boolean, boolean, boolean, boolean)
|
||||
*/
|
||||
public Future<?> stopPlayback(boolean toStoppedState) {
|
||||
return endPlayback(true, false, toStoppedState);
|
||||
return endPlayback(false, false, false, toStoppedState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that handles end of playback.
|
||||
*
|
||||
* Currently, it has 4 use cases:
|
||||
* Currently, it has 5 use cases:
|
||||
* <ul>
|
||||
* <li>Media playback has completed: call with (false, true, true)</li>
|
||||
* <li>User asks to skip to next episode: call with (true, true, true)</li>
|
||||
* <li>Stopping the media player: call with (true, false, true)</li>
|
||||
* <li>We want to change the media player implementation: call with (true, false, false)</li>
|
||||
* <li>Media playback has completed: call with (true, false, true, true)</li>
|
||||
* <li>User asks to skip to next episode: call with (false, true, true, true)</li>
|
||||
* <li>Skipping to next episode due to playback error: call with (false, false, true, true)</li>
|
||||
* <li>Stopping the media player: call with (false, false, false, true)</li>
|
||||
* <li>We want to change the media player implementation: call with (false, false, false, false)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param wasSkipped If true, we assume the current media's playback has ended, for
|
||||
* @param hasEnded If true, we assume the current media's playback has ended, for
|
||||
* purposes of post playback processing.
|
||||
* @param wasSkipped Whether the user chose to skip the episode (by pressing the skip
|
||||
* button).
|
||||
* @param shouldContinue If true, the media player should try to load, and possibly play,
|
||||
* the next item, based on the user preferences and whether such item
|
||||
* exists.
|
||||
|
@ -264,14 +268,15 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
*
|
||||
* @return a Future, just for the purpose of tracking its execution.
|
||||
*/
|
||||
protected abstract Future<?> endPlayback(boolean wasSkipped, boolean shouldContinue, boolean toStoppedState);
|
||||
protected abstract Future<?> endPlayback(boolean hasEnded, boolean wasSkipped,
|
||||
boolean shouldContinue, boolean toStoppedState);
|
||||
|
||||
/**
|
||||
* @return {@code true} if the WifiLock feature should be used, {@code false} otherwise.
|
||||
*/
|
||||
protected abstract boolean shouldLockWifi();
|
||||
|
||||
protected final synchronized void acquireWifiLockIfNecessary() {
|
||||
final synchronized void acquireWifiLockIfNecessary() {
|
||||
if (shouldLockWifi()) {
|
||||
if (wifiLock == null) {
|
||||
wifiLock = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
|
||||
|
@ -282,7 +287,7 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
protected final synchronized void releaseWifiLockIfNecessary() {
|
||||
final synchronized void releaseWifiLockIfNecessary() {
|
||||
if (wifiLock != null && wifiLock.isHeld()) {
|
||||
wifiLock.release();
|
||||
}
|
||||
|
@ -303,29 +308,28 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
* @param position The position to be set to the current Playable object in case playback started or paused.
|
||||
* Will be ignored if given the value of {@link #INVALID_TIME}.
|
||||
*/
|
||||
protected final synchronized void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia, int position) {
|
||||
final synchronized void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia, int position) {
|
||||
Log.d(TAG, this.getClass().getSimpleName() + ": Setting player status to " + newStatus);
|
||||
|
||||
PlayerStatus oldStatus = playerStatus;
|
||||
|
||||
this.oldPlayerStatus = playerStatus;
|
||||
this.playerStatus = newStatus;
|
||||
setPlayable(newMedia);
|
||||
|
||||
if (newMedia != null && newStatus != PlayerStatus.INDETERMINATE) {
|
||||
if (oldStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING) {
|
||||
if (oldPlayerStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING) {
|
||||
callback.onPlaybackPause(newMedia, position);
|
||||
} else if (oldStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING) {
|
||||
} else if (oldPlayerStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING) {
|
||||
callback.onPlaybackStart(newMedia, position);
|
||||
}
|
||||
}
|
||||
|
||||
callback.statusChanged(new PSMPInfo(playerStatus, getPlayable()));
|
||||
callback.statusChanged(new PSMPInfo(oldPlayerStatus, playerStatus, getPlayable()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setPlayerStatus(PlayerStatus, Playable, int)
|
||||
*/
|
||||
protected final void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia) {
|
||||
final void setPlayerStatus(@NonNull PlayerStatus newStatus, Playable newMedia) {
|
||||
setPlayerStatus(newStatus, newMedia, INVALID_TIME);
|
||||
}
|
||||
|
||||
|
@ -346,7 +350,7 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
|
||||
boolean onMediaPlayerError(Object inObj, int what, int extra);
|
||||
|
||||
void onPostPlayback(@NonNull Playable media, boolean ended, boolean playingNext);
|
||||
void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext);
|
||||
|
||||
void onPlaybackStart(@NonNull Playable playable, int position);
|
||||
|
||||
|
@ -361,10 +365,12 @@ public abstract class PlaybackServiceMediaPlayer {
|
|||
* Holds information about a PSMP object.
|
||||
*/
|
||||
public static class PSMPInfo {
|
||||
public PlayerStatus oldPlayerStatus;
|
||||
public PlayerStatus playerStatus;
|
||||
public Playable playable;
|
||||
|
||||
public PSMPInfo(PlayerStatus playerStatus, Playable playable) {
|
||||
PSMPInfo(PlayerStatus oldPlayerStatus, PlayerStatus playerStatus, Playable playable) {
|
||||
this.oldPlayerStatus = oldPlayerStatus;
|
||||
this.playerStatus = playerStatus;
|
||||
this.playable = playable;
|
||||
}
|
||||
|
|
|
@ -1063,7 +1063,7 @@ public final class DBReader {
|
|||
// reverse natural order: podcast with most unplayed episodes first
|
||||
return -1;
|
||||
} else if(counterLhs == counterRhs) {
|
||||
return lhs.getTitle().compareTo(rhs.getTitle());
|
||||
return lhs.getTitle().compareToIgnoreCase(rhs.getTitle());
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
@ -1077,7 +1077,7 @@ public final class DBReader {
|
|||
} else if(t2 == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return t1.toLowerCase().compareTo(t2.toLowerCase());
|
||||
return t1.compareToIgnoreCase(t2);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
|
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.storage;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -16,7 +17,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
|
@ -27,21 +28,29 @@ 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.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.GpodnetSyncService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
|
||||
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
|
||||
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.provider.Contacts.SettingsColumns.KEY;
|
||||
|
||||
/**
|
||||
* Provides methods for doing common tasks that use DBReader and DBWriter.
|
||||
*/
|
||||
public final class DBTasks {
|
||||
private static final String TAG = "DBTasks";
|
||||
|
||||
public static final String PREF_NAME = "dbtasks";
|
||||
private static final String PREF_LAST_REFRESH = "last_refresh";
|
||||
|
||||
/**
|
||||
* Executor service used by the autodownloadUndownloadedEpisodes method.
|
||||
*/
|
||||
|
@ -162,6 +171,9 @@ public final class DBTasks {
|
|||
}
|
||||
isRefreshing.set(false);
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
|
||||
|
||||
if (FlattrUtils.hasToken()) {
|
||||
Log.d(TAG, "Flattring all pending things.");
|
||||
new FlattrClickWorker(context).executeAsync(); // flattr pending things
|
||||
|
@ -313,6 +325,31 @@ public final class DBTasks {
|
|||
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the app should refresh all feeds, i.e. if the last auto refresh failed.
|
||||
*
|
||||
* The feeds are only refreshed if an update interval or time of day is set and the last
|
||||
* (successful) refresh was before the last interval or more than a day ago, respectively.
|
||||
*/
|
||||
public static void checkShouldRefreshFeeds(Context context) {
|
||||
long interval = 0;
|
||||
if(UserPreferences.getUpdateInterval() > 0) {
|
||||
interval = UserPreferences.getUpdateInterval();
|
||||
} else if(UserPreferences.getUpdateTimeOfDay().length > 0){
|
||||
interval = TimeUnit.DAYS.toMillis(1);
|
||||
}
|
||||
if(interval == 0) { // auto refresh is disabled
|
||||
return;
|
||||
}
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
long lastRefresh = prefs.getLong(PREF_LAST_REFRESH, 0);
|
||||
Log.d(TAG, "last refresh: " + Converter.getDurationStringLocalized(context,
|
||||
System.currentTimeMillis() - lastRefresh) + " ago");
|
||||
if(lastRefresh <= System.currentTimeMillis() - interval) {
|
||||
DBTasks.refreshAllFeeds(context, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the
|
||||
* DB and send a FeedUpdateBroadcast.
|
||||
|
|
|
@ -884,6 +884,17 @@ public class DBWriter {
|
|||
});
|
||||
}
|
||||
|
||||
public static Future<?> setFeedCustomTitle(Feed feed) {
|
||||
return dbExec.submit(() -> {
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
adapter.setFeedCustomTitle(feed.getId(), feed.getCustomTitle());
|
||||
adapter.close();
|
||||
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format an url for querying the database
|
||||
* (postfix a / and apply percent-encoding)
|
||||
|
|
|
@ -4,7 +4,9 @@ import android.content.Context;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
|
@ -51,15 +53,17 @@ public class FeedSearcher {
|
|||
task.run();
|
||||
}
|
||||
try {
|
||||
Set<Long> set = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < tasks.size(); i++) {
|
||||
FutureTask<List<FeedItem>> task = tasks.get(i);
|
||||
List<FeedItem> items = task.get();
|
||||
for (FeedItem item : items) {
|
||||
if (result.isEmpty() || !isDuplicate(result, item)) {
|
||||
if (!set.contains(item.getId())) { // to prevent duplicate results
|
||||
result.add(new SearchResult(item, values[i], subtitles[i]));
|
||||
set.add(item.getId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -67,20 +71,4 @@ public class FeedSearcher {
|
|||
Collections.sort(result, new SearchResultValueComparator());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the feed item is already in the search result list.
|
||||
*
|
||||
* @param result list of search results
|
||||
* @param item feed item to validate
|
||||
* @return true if the feed item is already in the results
|
||||
*/
|
||||
private static boolean isDuplicate(List<SearchResult> result, FeedItem item) {
|
||||
for (SearchResult resultItem : result) {
|
||||
if (resultItem.getComponent().getId() == item.getId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -57,6 +57,7 @@ public class PodDBAdapter {
|
|||
// Key-constants
|
||||
public static final String KEY_ID = "id";
|
||||
public static final String KEY_TITLE = "title";
|
||||
public static final String KEY_CUSTOM_TITLE = "custom_title";
|
||||
public static final String KEY_NAME = "name";
|
||||
public static final String KEY_LINK = "link";
|
||||
public static final String KEY_DESCRIPTION = "description";
|
||||
|
@ -109,14 +110,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
|
||||
|
@ -124,7 +125,7 @@ public class PodDBAdapter {
|
|||
|
||||
public static final String CREATE_TABLE_FEEDS = "CREATE TABLE "
|
||||
+ TABLE_NAME_FEEDS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
|
||||
+ " TEXT," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL + " TEXT,"
|
||||
+ " TEXT," + KEY_CUSTOM_TITLE + " TEXT," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL + " TEXT,"
|
||||
+ KEY_DOWNLOADED + " INTEGER," + KEY_LINK + " TEXT,"
|
||||
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
|
||||
+ KEY_LASTUPDATE + " TEXT," + KEY_LANGUAGE + " TEXT," + KEY_AUTHOR
|
||||
|
@ -225,6 +226,7 @@ public class PodDBAdapter {
|
|||
private static final String[] FEED_SEL_STD = {
|
||||
TABLE_NAME_FEEDS + "." + KEY_ID,
|
||||
TABLE_NAME_FEEDS + "." + KEY_TITLE,
|
||||
TABLE_NAME_FEEDS + "." + KEY_CUSTOM_TITLE,
|
||||
TABLE_NAME_FEEDS + "." + KEY_FILE_URL,
|
||||
TABLE_NAME_FEEDS + "." + KEY_DOWNLOAD_URL,
|
||||
TABLE_NAME_FEEDS + "." + KEY_DOWNLOADED,
|
||||
|
@ -363,7 +365,7 @@ public class PodDBAdapter {
|
|||
*/
|
||||
public long setFeed(Feed feed) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_TITLE, feed.getTitle());
|
||||
values.put(KEY_TITLE, feed.getFeedTitle());
|
||||
values.put(KEY_LINK, feed.getLink());
|
||||
values.put(KEY_DESCRIPTION, feed.getDescription());
|
||||
values.put(KEY_PAYMENT_LINK, feed.getPaymentLink());
|
||||
|
@ -854,6 +856,12 @@ public class PodDBAdapter {
|
|||
db.execSQL(sql);
|
||||
}
|
||||
|
||||
void setFeedCustomTitle(long feedId, String customTitle) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_CUSTOM_TITLE, customTitle);
|
||||
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(feedId)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts or updates a download status.
|
||||
*/
|
||||
|
@ -1436,15 +1444,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 +1476,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;
|
||||
|
||||
|
@ -1617,7 +1636,7 @@ public class PodDBAdapter {
|
|||
*/
|
||||
private static class PodDBHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final int VERSION = 1050004;
|
||||
private static final int VERSION = 1060200;
|
||||
|
||||
private Context context;
|
||||
|
||||
|
@ -1913,6 +1932,10 @@ public class PodDBAdapter {
|
|||
db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS
|
||||
+" SET " + PodDBAdapter.KEY_LASTUPDATE + "=NULL");
|
||||
}
|
||||
if(oldVersion < 1060200) {
|
||||
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
|
||||
+ " ADD COLUMN " + PodDBAdapter.KEY_CUSTOM_TITLE + " TEXT");
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(ProgressEvent.end());
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ 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;
|
||||
|
@ -86,34 +85,28 @@ public class SyndHandler extends DefaultHandler {
|
|||
state.defaultNamespaces.push(new NSAtom());
|
||||
} else if (prefix.equals(NSAtom.NSTAG)) {
|
||||
state.namespaces.put(uri, new NSAtom());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Recognized Atom namespace");
|
||||
Log.d(TAG, "Recognized Atom namespace");
|
||||
}
|
||||
} else if (uri.equals(NSContent.NSURI)
|
||||
&& prefix.equals(NSContent.NSTAG)) {
|
||||
state.namespaces.put(uri, new NSContent());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Recognized Content namespace");
|
||||
Log.d(TAG, "Recognized Content namespace");
|
||||
} else if (uri.equals(NSITunes.NSURI)
|
||||
&& prefix.equals(NSITunes.NSTAG)) {
|
||||
state.namespaces.put(uri, new NSITunes());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Recognized ITunes namespace");
|
||||
Log.d(TAG, "Recognized ITunes namespace");
|
||||
} else if (uri.equals(NSSimpleChapters.NSURI)
|
||||
&& prefix.matches(NSSimpleChapters.NSTAG)) {
|
||||
state.namespaces.put(uri, new NSSimpleChapters());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Recognized SimpleChapters namespace");
|
||||
Log.d(TAG, "Recognized SimpleChapters namespace");
|
||||
} else if (uri.equals(NSMedia.NSURI)
|
||||
&& prefix.equals(NSMedia.NSTAG)) {
|
||||
state.namespaces.put(uri, new NSMedia());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Recognized media namespace");
|
||||
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");
|
||||
Log.d(TAG, "Recognized DublinCore namespace");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@ public class NSMedia extends Namespace {
|
|||
private static final String MIME_TYPE = "type";
|
||||
private static final String DURATION = "duration";
|
||||
private static final String DEFAULT = "isDefault";
|
||||
private static final String MEDIUM = "medium";
|
||||
|
||||
private static final String MEDIUM_IMAGE = "image";
|
||||
private static final String MEDIUM_AUDIO = "audio";
|
||||
private static final String MEDIUM_VIDEO = "video";
|
||||
|
||||
private static final String IMAGE = "thumbnail";
|
||||
private static final String IMAGE_URL = "url";
|
||||
|
@ -40,20 +45,31 @@ public class NSMedia extends Namespace {
|
|||
String url = attributes.getValue(DOWNLOAD_URL);
|
||||
String type = attributes.getValue(MIME_TYPE);
|
||||
String defaultStr = attributes.getValue(DEFAULT);
|
||||
boolean validType;
|
||||
String medium = attributes.getValue(MEDIUM);
|
||||
boolean validTypeMedia = false;
|
||||
boolean validTypeImage = false;
|
||||
|
||||
boolean isDefault = "true".equals(defaultStr);
|
||||
|
||||
if (SyndTypeUtils.enclosureTypeValid(type)) {
|
||||
validType = true;
|
||||
if (MEDIUM_AUDIO.equals(medium) || MEDIUM_VIDEO.equals(medium)) {
|
||||
validTypeMedia = true;
|
||||
} else if (MEDIUM_IMAGE.equals(medium)) {
|
||||
validTypeImage = true;
|
||||
} else {
|
||||
type = SyndTypeUtils.getValidMimeTypeFromUrl(url);
|
||||
validType = type != null;
|
||||
if (type == null) {
|
||||
type = SyndTypeUtils.getMimeTypeFromUrl(url);
|
||||
}
|
||||
|
||||
if (SyndTypeUtils.enclosureTypeValid(type)) {
|
||||
validTypeMedia = true;
|
||||
} else if (SyndTypeUtils.imageTypeValid(type)) {
|
||||
validTypeImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.getCurrentItem() != null &&
|
||||
(state.getCurrentItem().getMedia() == null || isDefault) &&
|
||||
url != null && validType) {
|
||||
url != null && validTypeMedia) {
|
||||
long size = 0;
|
||||
String sizeStr = attributes.getValue(SIZE);
|
||||
try {
|
||||
|
@ -77,6 +93,12 @@ public class NSMedia extends Namespace {
|
|||
media.setDuration(durationMs);
|
||||
}
|
||||
state.getCurrentItem().setMedia(media);
|
||||
} else if (state.getCurrentItem() != null && url != null && validTypeImage) {
|
||||
FeedImage image = new FeedImage();
|
||||
image.setDownload_url(url);
|
||||
image.setOwner(state.getCurrentItem());
|
||||
|
||||
state.getCurrentItem().setImage(image);
|
||||
}
|
||||
} else if (IMAGE.equals(localName)) {
|
||||
String url = attributes.getValue(IMAGE_URL);
|
||||
|
|
|
@ -20,25 +20,27 @@ import de.danoeh.antennapod.core.util.DateUtils;
|
|||
*
|
||||
*/
|
||||
public class NSRSS20 extends Namespace {
|
||||
private static final String TAG = "NSRSS20";
|
||||
public static final String NSTAG = "rss";
|
||||
public static final String NSURI = "";
|
||||
|
||||
public static final String CHANNEL = "channel";
|
||||
private static final String TAG = "NSRSS20";
|
||||
|
||||
private static final String NSTAG = "rss";
|
||||
private static final String NSURI = "";
|
||||
|
||||
public static final String CHANNEL = "channel";
|
||||
public static final String ITEM = "item";
|
||||
public static final String GUID = "guid";
|
||||
public static final String TITLE = "title";
|
||||
public static final String LINK = "link";
|
||||
public static final String DESCR = "description";
|
||||
public static final String PUBDATE = "pubDate";
|
||||
public static final String ENCLOSURE = "enclosure";
|
||||
public static final String IMAGE = "image";
|
||||
public static final String URL = "url";
|
||||
public static final String LANGUAGE = "language";
|
||||
private static final String GUID = "guid";
|
||||
private static final String TITLE = "title";
|
||||
private static final String LINK = "link";
|
||||
private static final String DESCR = "description";
|
||||
private static final String PUBDATE = "pubDate";
|
||||
private static final String ENCLOSURE = "enclosure";
|
||||
private static final String IMAGE = "image";
|
||||
private static final String URL = "url";
|
||||
private static final String LANGUAGE = "language";
|
||||
|
||||
public static final String ENC_URL = "url";
|
||||
public static final String ENC_LEN = "length";
|
||||
public static final String ENC_TYPE = "type";
|
||||
private static final String ENC_URL = "url";
|
||||
private static final String ENC_LEN = "length";
|
||||
private static final String ENC_TYPE = "type";
|
||||
|
||||
@Override
|
||||
public SyndElement handleElementStart(String localName, HandlerState state,
|
||||
|
@ -51,15 +53,16 @@ public class NSRSS20 extends Namespace {
|
|||
} else if (ENCLOSURE.equals(localName)) {
|
||||
String type = attributes.getValue(ENC_TYPE);
|
||||
String url = attributes.getValue(ENC_URL);
|
||||
boolean validType;
|
||||
if(SyndTypeUtils.enclosureTypeValid(type)) {
|
||||
validType = true;
|
||||
} else {
|
||||
type = type = SyndTypeUtils.getValidMimeTypeFromUrl(url);
|
||||
validType = type != null;
|
||||
}
|
||||
if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null &&
|
||||
validType) {
|
||||
|
||||
boolean validType = SyndTypeUtils.enclosureTypeValid(type);
|
||||
if(!validType) {
|
||||
type = SyndTypeUtils.getMimeTypeFromUrl(url);
|
||||
validType = SyndTypeUtils.enclosureTypeValid(type);
|
||||
}
|
||||
|
||||
boolean validUrl = !TextUtils.isEmpty(url);
|
||||
if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null &&
|
||||
validType && validUrl) {
|
||||
long size = 0;
|
||||
try {
|
||||
size = Long.parseLong(attributes.getValue(ENC_LEN));
|
||||
|
@ -70,8 +73,8 @@ public class NSRSS20 extends Namespace {
|
|||
} catch (NumberFormatException e) {
|
||||
Log.d(TAG, "Length attribute could not be parsed.");
|
||||
}
|
||||
state.getCurrentItem().setMedia(
|
||||
new FeedMedia(state.getCurrentItem(), url, size, type));
|
||||
FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type);
|
||||
state.getCurrentItem().setMedia(media);
|
||||
}
|
||||
|
||||
} else if (IMAGE.equals(localName)) {
|
||||
|
|
|
@ -30,6 +30,7 @@ public class NSAtom extends Namespace {
|
|||
private static final String AUTHOR = "author";
|
||||
private static final String AUTHOR_NAME = "name";
|
||||
private static final String CONTENT = "content";
|
||||
private static final String SUMMARY = "summary";
|
||||
private static final String IMAGE_LOGO = "logo";
|
||||
private static final String IMAGE_ICON = "icon";
|
||||
private static final String SUBTITLE = "subtitle";
|
||||
|
@ -44,6 +45,7 @@ public class NSAtom extends Namespace {
|
|||
private static final String LINK_LENGTH = "length";
|
||||
// rel-values
|
||||
private static final String LINK_REL_ALTERNATE = "alternate";
|
||||
private static final String LINK_REL_ARCHIVES = "archives";
|
||||
private static final String LINK_REL_ENCLOSURE = "enclosure";
|
||||
private static final String LINK_REL_PAYMENT = "payment";
|
||||
private static final String LINK_REL_RELATED = "related";
|
||||
|
@ -59,8 +61,8 @@ public class NSAtom extends Namespace {
|
|||
/**
|
||||
* Regexp to test whether an Element is a Text Element.
|
||||
*/
|
||||
private static final String isText = TITLE + "|" + CONTENT + "|" + "|"
|
||||
+ SUBTITLE;
|
||||
private static final String isText = TITLE + "|" + CONTENT + "|"
|
||||
+ SUBTITLE + "|" + SUMMARY;
|
||||
|
||||
public static final String isFeed = FEED + "|" + NSRSS20.CHANNEL;
|
||||
public static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM;
|
||||
|
@ -93,14 +95,12 @@ public class NSAtom extends Namespace {
|
|||
Log.d(TAG, "Length attribute could not be parsed.");
|
||||
}
|
||||
String type = attributes.getValue(LINK_TYPE);
|
||||
boolean validType;
|
||||
if(SyndTypeUtils.enclosureTypeValid(type)) {
|
||||
validType = true;
|
||||
} else {
|
||||
type = SyndTypeUtils.getValidMimeTypeFromUrl(href);
|
||||
validType = type != null;
|
||||
|
||||
if (type == null) {
|
||||
type = SyndTypeUtils.getMimeTypeFromUrl(href);
|
||||
}
|
||||
if (validType) {
|
||||
|
||||
if(SyndTypeUtils.enclosureTypeValid(type)) {
|
||||
FeedItem currItem = state.getCurrentItem();
|
||||
if(currItem != null && !currItem.hasMedia()) {
|
||||
currItem.setMedia(new FeedMedia(currItem, href, size, type));
|
||||
|
@ -129,6 +129,17 @@ public class NSAtom extends Namespace {
|
|||
}
|
||||
state.addAlternateFeedUrl(title, href);
|
||||
}
|
||||
} else if (LINK_REL_ARCHIVES.equals(rel) && state.getFeed() != null) {
|
||||
String type = attributes.getValue(LINK_TYPE);
|
||||
if (LINK_TYPE_ATOM.equals(type) || LINK_TYPE_RSS.equals(type)) {
|
||||
String title = attributes.getValue(LINK_TITLE);
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
title = href;
|
||||
}
|
||||
state.addAlternateFeedUrl(title, href);
|
||||
} else if (LINK_TYPE_HTML.equals(type) || LINK_TYPE_XHTML.equals(type)) {
|
||||
//A Link such as to a directory such as iTunes
|
||||
}
|
||||
} else if (LINK_REL_PAYMENT.equals(rel) && state.getFeed() != null) {
|
||||
state.getFeed().setPaymentLink(href);
|
||||
} else if (LINK_REL_NEXT.equals(rel) && state.getFeed() != null) {
|
||||
|
@ -191,6 +202,9 @@ public class NSAtom extends Namespace {
|
|||
} else if (CONTENT.equals(top) && ENTRY.equals(second) && textElement != null &&
|
||||
state.getCurrentItem() != null) {
|
||||
state.getCurrentItem().setDescription(textElement.getProcessedContent());
|
||||
} else if (SUMMARY.equals(top) && ENTRY.equals(second) && textElement != null &&
|
||||
state.getCurrentItem() != null && state.getCurrentItem().getDescription() == null) {
|
||||
state.getCurrentItem().setDescription(textElement.getProcessedContent());
|
||||
} else if (UPDATED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null &&
|
||||
state.getCurrentItem().getPubDate() == null) {
|
||||
state.getCurrentItem().setPubDate(DateUtils.parse(content));
|
||||
|
@ -200,9 +214,13 @@ public class NSAtom extends Namespace {
|
|||
state.getFeed().setImage(new FeedImage(state.getFeed(), content, null));
|
||||
} else if (IMAGE_ICON.equals(top) && state.getFeed() != null) {
|
||||
state.getFeed().setImage(new FeedImage(state.getFeed(), content, null));
|
||||
} else if (AUTHOR.equals(second) && state.getFeed() != null) {
|
||||
if (AUTHOR_NAME.equals(top)) {
|
||||
} else if (AUTHOR_NAME.equals(top) && AUTHOR.equals(second) &&
|
||||
state.getFeed() != null && state.getCurrentItem() == null) {
|
||||
String currentName = state.getFeed().getAuthor();
|
||||
if (currentName == null) {
|
||||
state.getFeed().setAuthor(content);
|
||||
} else {
|
||||
state.getFeed().setAuthor(currentName + ", " + content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
package de.danoeh.antennapod.core.syndication.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Utility class for handling MIME-Types of enclosures */
|
||||
public class SyndTypeUtils {
|
||||
|
||||
private static final String VALID_MIMETYPE = "audio/.*" + "|" + "video/.*"
|
||||
+ "|" + "application/ogg";
|
||||
private static final String VALID_MEDIA_MIMETYPE = TextUtils.join("|", Arrays.asList(
|
||||
"audio/.*",
|
||||
"video/.*",
|
||||
"application/ogg",
|
||||
"application/octet-stream"));
|
||||
|
||||
private static final String VALID_IMAGE_MIMETYPE = "image/.*";
|
||||
|
||||
private SyndTypeUtils() {
|
||||
|
||||
|
@ -17,9 +26,17 @@ public class SyndTypeUtils {
|
|||
if (type == null) {
|
||||
return false;
|
||||
} else {
|
||||
return type.matches(VALID_MIMETYPE);
|
||||
return type.matches(VALID_MEDIA_MIMETYPE);
|
||||
}
|
||||
}
|
||||
public static boolean imageTypeValid(String type) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
} else {
|
||||
return type.matches(VALID_IMAGE_MIMETYPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should be used if mime-type of enclosure tag is not supported. This
|
||||
|
@ -27,15 +44,27 @@ public class SyndTypeUtils {
|
|||
* the type is not supported, this method will return null.
|
||||
*/
|
||||
public static String getValidMimeTypeFromUrl(String url) {
|
||||
if (url != null) {
|
||||
String extension = FilenameUtils.getExtension(url);
|
||||
if (extension != null) {
|
||||
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
if (type != null && enclosureTypeValid(type)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
String type = getMimeTypeFromUrl(url);
|
||||
if (enclosureTypeValid(type)) {
|
||||
return type;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be used if mime-type of enclosure tag is not supported. This
|
||||
* method will return the mime-type of the file extension.
|
||||
*/
|
||||
public static String getMimeTypeFromUrl(String url) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
String extension = FilenameUtils.getExtension(url);
|
||||
if (extension == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,6 @@ import android.net.wifi.WifiManager;
|
|||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
@ -22,6 +17,9 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
|
|||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import rx.Observable;
|
||||
import rx.Subscriber;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
package de.danoeh.antennapod.core.util.comparator;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.SearchResult;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class SearchResultValueComparator implements Comparator<SearchResult> {
|
||||
|
||||
/**
|
||||
* Compare items based, first, on where they were found (ie. title, chapters, or show notes).
|
||||
* If they were found in the same section, then compare based on the title, in lexicographic
|
||||
* order. This is still not ideal since, for example, "#12 Example A" would be considered
|
||||
* before "#8 Example B" due to the fact that "8" has a larger unicode value than "1"
|
||||
*/
|
||||
@Override
|
||||
public int compare(SearchResult lhs, SearchResult rhs) {
|
||||
return rhs.getValue() - lhs.getValue();
|
||||
int value = rhs.getValue() - lhs.getValue();
|
||||
if (value == 0 && lhs.getComponent() instanceof FeedItem && rhs.getComponent() instanceof FeedItem) {
|
||||
String lhsTitle = ((FeedItem) lhs.getComponent()).getTitle();
|
||||
String rhsTitle = ((FeedItem) rhs.getComponent()).getTitle();
|
||||
return lhsTitle.compareTo(rhsTitle);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package de.danoeh.antennapod.core.util.syndication;
|
||||
|
||||
import org.jsoup.helper.StringUtil;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.select.NodeTraversor;
|
||||
import org.jsoup.select.NodeVisitor;
|
||||
|
||||
/**
|
||||
* This class is based on <code>HtmlToPlainText</code> from jsoup's examples package.
|
||||
*
|
||||
* HTML to plain-text. This example program demonstrates the use of jsoup to convert HTML input to lightly-formatted
|
||||
* plain-text. That is divergent from the general goal of jsoup's .text() methods, which is to get clean data from a
|
||||
* scrape.
|
||||
* <p>
|
||||
* Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend.
|
||||
* </p>
|
||||
* <p>
|
||||
* To invoke from the command line, assuming you've downloaded the jsoup jar to your current directory:</p>
|
||||
* <p><code>java -cp jsoup.jar org.jsoup.examples.HtmlToPlainText url [selector]</code></p>
|
||||
* where <i>url</i> is the URL to fetch, and <i>selector</i> is an optional CSS selector.
|
||||
*
|
||||
* @author Jonathan Hedley, jonathan@hedley.net
|
||||
* @author AntennaPod open source community
|
||||
*/
|
||||
public class HtmlToPlainText {
|
||||
|
||||
/**
|
||||
* Format an Element to plain-text
|
||||
* @param element the root element to format
|
||||
* @return formatted text
|
||||
*/
|
||||
public String getPlainText(Element element) {
|
||||
FormattingVisitor formatter = new FormattingVisitor();
|
||||
NodeTraversor traversor = new NodeTraversor(formatter);
|
||||
traversor.traverse(element); // walk the DOM, and call .head() and .tail() for each node
|
||||
|
||||
return formatter.toString();
|
||||
}
|
||||
|
||||
// the formatting rules, implemented in a breadth-first DOM traverse
|
||||
private class FormattingVisitor implements NodeVisitor {
|
||||
|
||||
private StringBuilder accum = new StringBuilder(); // holds the accumulated text
|
||||
|
||||
// hit when the node is first seen
|
||||
public void head(Node node, int depth) {
|
||||
String name = node.nodeName();
|
||||
if (node instanceof TextNode) {
|
||||
append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM.
|
||||
}
|
||||
else if (name.equals("li")) {
|
||||
append("\n * ");
|
||||
}
|
||||
else if (name.equals("dt")) {
|
||||
append(" ");
|
||||
}
|
||||
else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5", "tr")) {
|
||||
append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// hit when all of the node's children (if any) have been visited
|
||||
public void tail(Node node, int depth) {
|
||||
String name = node.nodeName();
|
||||
if (StringUtil.in(name, "br", "dd", "dt", "p", "h1", "h2", "h3", "h4", "h5")) {
|
||||
append("\n");
|
||||
} else if (name.equals("a")) {
|
||||
append(String.format(" <%s>", node.absUrl("href")));
|
||||
}
|
||||
}
|
||||
|
||||
// appends text to the string builder with a simple word wrap method
|
||||
private void append(String text) {
|
||||
if (text.equals(" ") &&
|
||||
(accum.length() == 0 || StringUtil.in(accum.substring(accum.length() - 1), " ", "\n"))) {
|
||||
return; // don't accumulate long runs of empty spaces
|
||||
}
|
||||
|
||||
accum.append(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return accum.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:drawable="@color/overlay_dark"/>
|
||||
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_launcher"/>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue