Merge branch 'develop'

This commit is contained in:
Martin Fietz 2017-10-17 22:55:30 +02:00
commit e884e989c7
489 changed files with 14903 additions and 5946 deletions

1
.gitignore vendored
View File

@ -42,3 +42,4 @@ libs
src/de/danoeh/antennapod/util/flattr/FlattrConfig.java
gradle.properties
*.keystore
*.p12

View File

@ -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
@ -14,18 +15,23 @@ trans.de = core/src/main/res/values-de/strings.xml
trans.el = core/src/main/res/values-el/strings.xml
trans.es = core/src/main/res/values-es/strings.xml
trans.es_ES = core/src/main/res/values-es-rES/strings.xml
trans.et = core/src/main/res/values-et/strings.xml
trans.fa = core/src/main/res/values-fa/strings.xml
trans.fi = core/src/main/res/values-fi/strings.xml
trans.fr = core/src/main/res/values-fr/strings.xml
trans.gl = core/src/main/res/values-gl-rES/strings.xml
trans.he_IL = core/src/main/res/values-iw-rIL/strings.xml
trans.hi_IN = core/src/main/res/values-hi-rIN/strings.xml
trans.hu = core/src/main/res/values-hu/strings.xml
trans.id = core/src/main/res/values-id/strings.xml
trans.it = core/src/main/res/values-it/strings.xml
trans.it_IT = core/src/main/res/values-it-rIT/strings.xml
trans.is = core/src/main/res/values-is-rIS/strings.xml
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
@ -38,20 +44,29 @@ trans.ro_RO = core/src/main/res/values-ro-rRO/strings.xml
trans.ru = core/src/main/res/values-ru/strings.xml
trans.ru-RU = core/src/main/res/values-ru/strings.xml
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.sw_KE = core/src/main/res/values-sw-rKE/strings.xml
trans.te = core/src/main/res/values-te/strings.xml
trans.tr = core/src/main/res/values-tr/strings.xml
trans.uk_UA = core/src/main/res/values-uk-rUA/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 = core/src/main/res/values-zh/strings.xml
trans.zh_CN = core/src/main/res/values-zh-rCN/strings.xml
trans.zh_TW = core/src/main/res/values-zh-rTW/strings.xml
trans.zh_HK = core/src/main/res/values-zh-rHK/strings.xml
[antennapod.description]
file_filter = description/<lang>.txt
source_file = description/en.txt
file_filter = app/src/main/play/<lang>/listing/fulldescription
source_file = app/src/main/play/en-US/listing/fulldescription
source_lang = en
[antennapod.shortdescription]
file_filter = app/src/main/play/<lang>/listing/shortdescription
source_file = app/src/main/play/en-US/listing/shortdescription
source_lang = en
[antennapod.changelog]
file_filter = changelog/<lang>.md
source_file = CHANGELOG.md
file_filter = app/src/main/play/<lang>/whatsnew
source_file = app/src/main/play/en-US/whatsnew
source_lang = en

View File

@ -1,20 +1,62 @@
Change Log
==========
Version 1.6.3
-------------
* New features:
* Support for Android Auto
* Sort feeds by number of played episodes
* Statistics modes
* Setting: Enqueue downloaded
* Launch screen
* Improvements
* Chapter duration
* Feed title in deletion confirmation
* Fixes:
* Episodes refresh spinner
* Publication date parsing
* Unknown mime type
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
* 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
* Audioplayer controls
* Audio ducking
* Video control fade-out
* External media controls
* Feed parsing
Version 1.5.0
-------------

View File

@ -7,11 +7,11 @@ In particular, please not only tell us that you want something (*what*), but als
---
**App version:** 1.x (from Google Play/F-Store/Custom build)
**App version:** 1.x (from Google Play/F-Droid/Custom build)
**Android version**: 5.x [Please mention if you are using a custom rom!]
**Devide model**:
**Device model**:
**Expected behaviour**:

View File

@ -2,8 +2,12 @@
This is the official repository of AntennaPod, the easy-to-use, flexible and open-source podcast manager for Android.
[![Download from Google Play](http://www.android.com/images/brand/android_app_on_play_large.png "Download from Google Play")](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)
[![AntennaPod on fdroid.org](https://camo.githubusercontent.com/7df0eafa4433fa4919a56f87c3d99cf81b68d01c/68747470733a2f2f662d64726f69642e6f72672f77696b692f696d616765732f632f63342f462d44726f69642d627574746f6e5f617661696c61626c652d6f6e2e706e67 "Download from fdroid.org")](https://f-droid.org/repository/browse/?fdid=de.danoeh.antennapod)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="90">](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="90">](https://f-droid.org/app/de.danoeh.antennapod)
## Feedback
You can use the [AntennaPod Google Group](https://groups.google.com/forum/#!forum/antennapod) for discussions about the app.
@ -17,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

View File

@ -2,50 +2,13 @@ import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: "com.android.application"
apply plugin: "me.tatarka.retrolambda"
apply plugin: 'com.github.triplet.play'
repositories {
maven { url "https://jitpack.io" }
mavenCentral()
}
dependencies {
compile project(":core")
compile "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "com.android.support:gridlayout-v7:$supportVersion"
compile "com.android.support:percent:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion"
compile "org.apache.commons:commons-lang3:$commonslangVersion"
compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"
}
compile "commons-io:commons-io:$commonsioVersion"
compile "org.jsoup:jsoup:$jsoupVersion"
compile "com.github.bumptech.glide:glide:$glideVersion"
compile "com.squareup.okhttp:okhttp:$okhttpVersion"
compile "com.squareup.okhttp:okhttp-urlconnection:$okhttpVersion"
compile "com.squareup.okio:okio:$okioVersion"
compile "de.greenrobot:eventbus:$eventbusVersion"
compile "io.reactivex:rxandroid:$rxAndroidVersion"
compile "io.reactivex:rxjava:$rxJavaVersion"
// And ProGuard rules for RxJava!
compile "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
compile "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
compile "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
compile("com.github.afollestad.material-dialogs:commons:$materialDialogsVersion") {
transitive = true
}
compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
compile("com.githang:viewpagerindicator:2.5@aar") {
exclude module: "support-v4"
}
compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
}
def getMyVersionName() {
def parsedManifestXml = (new XmlSlurper())
.parse("${projectDir}/src/main/AndroidManifest.xml")
@ -67,6 +30,7 @@ android {
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode getMyVersionCode()
versionName "${getMyVersionName()}"
testApplicationId "de.test.antennapod"
@ -137,6 +101,78 @@ android {
aaptOptions {
additionalParameters "--no-version-vectors"
}
productFlavors {
free {
}
play {
}
}
dexOptions {
jumboMode true
}
}
configurations {
freeDebugCompile
freeReleaseCompile
playDebugCompile
playReleaseCompile
}
dependencies {
freeDebugCompile project(path: ":core", configuration: "freeDebug")
freeReleaseCompile project(path: ":core", configuration: "freeRelease")
// free build hack: skip some dependencies
if (!doFreeBuild()) {
playDebugCompile project(path: ":core", configuration: "playDebug")
playReleaseCompile project(path: ":core", configuration: "playRelease")
} else {
System.out.println("app: free build hack, skipping some dependencies")
}
compile 'com.android.support:multidex:1.0.1'
compile "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "com.android.support:gridlayout-v7:$supportVersion"
compile "com.android.support:percent:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion"
compile "org.apache.commons:commons-lang3:$commonslangVersion"
compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"
}
compile "commons-io:commons-io:$commonsioVersion"
compile "org.jsoup:jsoup:$jsoupVersion"
compile "com.github.bumptech.glide:glide:$glideVersion"
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"
compile "io.reactivex:rxjava:$rxJavaVersion"
// And ProGuard rules for RxJava!
compile "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
compile "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
compile "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
compile("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
transitive = true
}
compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
compile("com.githang:viewpagerindicator:2.5@aar") {
exclude module: "support-v4"
}
compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
compile 'com.github.mfietz:fyydlin:v0.1'
}
play {
serviceAccountEmail = '522080222319-compute@developer.gserviceaccount.com'
pk12File = file('../serviceaccount-c3d7d0f61387.p12')
}
// about.html is templatized so that we can automatically insert
@ -145,12 +181,13 @@ task filterAbout {
inputs.files files(["src/main/templates/about.html",
"src/main/AndroidManifest.xml"])
outputs.file "src/main/assets/about.html"
} << {
} doLast {
copy {
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')])
}
}
@ -164,3 +201,11 @@ task copyTextFiles(type: Copy) {
}
preBuild.dependsOn filterAbout, copyTextFiles
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint" << "-Xlint:-deprecation" << "-Xlint:-serial"
}
}
}

View File

@ -53,7 +53,7 @@
public *;
}
-dontwarn com.squareup.okhttp.**
-dontwarn okhttp3.**
-dontwarn okio.**
# for RxJava:
@ -66,8 +66,10 @@
-keep interface android.support.v4.** { *; }
-keep class !android.support.v7.internal.view.menu.**,android.support.v7.** {*;}
-keep interface android.support.v7.** { *; }
-keep class com.google.android.wearable.** { *; }
-dontwarn android.support.v4.**
-dontwarn android.support.v7.**
-dontwarn com.google.android.wearable.**
-keepattributes *Annotation*
@ -107,4 +109,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 { *; }

View File

@ -1,6 +1,8 @@
package de.test.antennapod.service.playback;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.test.InstrumentationTestCase;
import junit.framework.AssertionFailedError;
@ -20,6 +22,7 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.LocalPSMP;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
@ -112,7 +115,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testInit() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, defaultCallback);
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, new DefaultPSMPCallback());
psmp.shutdown();
}
@ -138,7 +141,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectStreamNoStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -159,47 +162,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@ -217,7 +179,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectStreamStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -238,46 +200,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@ -296,7 +218,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectStreamNoStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(4);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -320,46 +242,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@ -376,7 +258,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectStreamStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(5);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -403,47 +285,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@ -459,7 +300,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectLocalNoStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -480,47 +321,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -537,7 +337,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectLocalStartNoPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(2);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -558,46 +358,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -614,7 +374,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectLocalNoStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(4);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -638,46 +398,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = e;
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -693,7 +413,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
public void testPlayMediaObjectLocalStartPrepare() throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final CountDownLatch countDownLatch = new CountDownLatch(5);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
try {
@ -721,46 +441,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
countDownLatch.countDown();
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -773,58 +453,12 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
psmp.shutdown();
}
private final PlaybackServiceMediaPlayer.PSMPCallback defaultCallback = new PlaybackServiceMediaPlayer.PSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) { return false; }
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
private void pauseTestSkeleton(final PlayerStatus initialState, final boolean stream, final boolean abandonAudioFocus, final boolean reinit, long timeoutSeconds) throws InterruptedException {
final Context c = getInstrumentation().getTargetContext();
final int latchCount = (stream && reinit) ? 2 : 1;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@ -863,42 +497,12 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = new AssertionFailedError("Unexpected call to shouldStop");
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
if (assertionError == null)
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -955,7 +559,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
(initialState == PlayerStatus.PREPARED) ? 1 : 0;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@ -973,36 +577,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
if (assertionError == null) {
@ -1010,11 +584,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
@ -1048,7 +617,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
final Context c = getInstrumentation().getTargetContext();
final int latchCount = 1;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@ -1062,37 +631,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
countDownLatch.countDown();
}
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
@ -1101,11 +639,6 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -1153,7 +686,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
final Context c = getInstrumentation().getTargetContext();
final int latchCount = 2;
final CountDownLatch countDownLatch = new CountDownLatch(latchCount);
PlaybackServiceMediaPlayer.PSMPCallback callback = new PlaybackServiceMediaPlayer.PSMPCallback() {
PlaybackServiceMediaPlayer.PSMPCallback callback = new DefaultPSMPCallback() {
@Override
public void statusChanged(LocalPSMP.PSMPInfo newInfo) {
checkPSMPInfo(newInfo);
@ -1169,47 +702,12 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public boolean onMediaPlayerInfo(int code, int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
if (assertionError == null)
assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
return false;
}
@Override
public boolean endPlayback(Playable p, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
return false;
}
};
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@ -1248,4 +746,71 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
super("Unexpected state change: " + status);
}
}
private class DefaultPSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
@Override
public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
}
@Override
public void shouldStop() {
}
@Override
public void playbackSpeedChanged(float s) {
}
@Override
public void setSpeedAbilityChanged() {
}
@Override
public void onBufferingUpdate(int percent) {
}
@Override
public void onMediaChanged(boolean reloadUI) {
}
@Override
public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
return false;
}
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
return false;
}
@Override
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
}
@Override
public void onPlaybackStart(@NonNull Playable playable, int position) {
}
@Override
public void onPlaybackPause(Playable playable, int position) {
}
@Override
public Playable getNextInQueue(Playable currentMedia) {
return null;
}
@Override
public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
}
}
}

View File

@ -218,58 +218,43 @@ public class DBReaderTest extends InstrumentationTestCase {
}
}
private List<FeedItem> saveUnreadItems(int numItems) {
if (numItems <= 0) {
throw new IllegalArgumentException("numItems<=0");
}
private List<FeedItem> saveNewItems(int numItems) {
List<Feed> feeds = saveFeedlist(numItems, numItems, true);
List<FeedItem> items = new ArrayList<>();
for (Feed f : feeds) {
items.addAll(f.getItems());
}
List<FeedItem> unread = new ArrayList<>();
List<FeedItem> newItems = new ArrayList<>();
Random random = new Random();
while (unread.size() < numItems) {
while (newItems.size() < numItems) {
int i = random.nextInt(numItems);
if (!unread.contains(items.get(i))) {
if (!newItems.contains(items.get(i))) {
FeedItem item = items.get(i);
item.setPlayed(false);
unread.add(item);
item.setNew();
newItems.add(item);
}
}
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemlist(unread);
adapter.setFeedItemlist(newItems);
adapter.close();
return unread;
}
public void testGetUnreadItemsList() {
final int numItems = 10;
List<FeedItem> unread = saveUnreadItems(numItems);
List<FeedItem> unreadSaved = DBReader.getUnreadItemsList();
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for (FeedItem item : unreadSaved) {
assertFalse(item.isPlayed());
}
return newItems;
}
public void testGetNewItemIds() {
final int numItems = 10;
List<FeedItem> unread = saveUnreadItems(numItems);
long[] unreadIds = new long[unread.size()];
for (int i = 0; i < unread.size(); i++) {
unreadIds[i] = unread.get(i).getId();
List<FeedItem> newItems = saveNewItems(numItems);
long[] unreadIds = new long[newItems.size()];
for (int i = 0; i < newItems.size(); i++) {
unreadIds[i] = newItems.get(i).getId();
}
List<FeedItem> unreadSaved = DBReader.getUnreadItemsList();
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for(int i=0; i < unreadSaved.size(); i++) {
long savedId = unreadSaved.get(i).getId();
List<FeedItem> newItemsSaved = DBReader.getNewItemsList();
assertNotNull(newItemsSaved);
assertTrue(newItems.size() == newItemsSaved.size());
for(int i=0; i < newItemsSaved.size(); i++) {
long savedId = newItemsSaved.get(i).getId();
boolean found = false;
for (long id : unreadIds) {
if (id == savedId) {

View File

@ -45,7 +45,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++) {

View File

@ -138,7 +138,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
ListView list = (ListView) solo.getView(R.id.nav_list);
for (int i = 0; i < uiTestUtils.hostedFeeds.size(); i++) {
Feed f = uiTestUtils.hostedFeeds.get(i);
solo.clickOnScreen(50, 50); // open nav drawer
openNavDrawer();
solo.scrollListToLine(list, i);
solo.clickOnText(f.getTitle());
solo.waitForView(android.R.id.list);

View File

@ -245,10 +245,10 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
}, Timeout.getSmallTimeout());
assertTrue(startedPlaying);
boolean stoppedPlaying = solo.waitForCondition(() -> {
return uiTestUtils.getCurrentMedia(getActivity()) == null
|| uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId;
}, Timeout.getLargeTimeout());
boolean stoppedPlaying = solo.waitForCondition(() ->
uiTestUtils.getCurrentMedia(getActivity()) == null
|| uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId
, Timeout.getLargeTimeout());
assertTrue(stoppedPlaying);
startLocalPlayback();

View File

@ -236,10 +236,9 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
}, Timeout.getSmallTimeout());
assertTrue(startedPlaying);
boolean stoppedPlaying = solo.waitForCondition(() -> {
return uiTestUtils.getCurrentMedia(getActivity()) == null
|| uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId;
}, Timeout.getLargeTimeout());
boolean stoppedPlaying = solo.waitForCondition(() ->
uiTestUtils.getCurrentMedia(getActivity()) == null
|| uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId, Timeout.getLargeTimeout());
assertTrue(stoppedPlaying);
startLocalPlayback();

View File

@ -3,10 +3,12 @@ package de.test.antennapod.ui;
import android.content.Context;
import android.content.res.Resources;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
@ -362,4 +364,48 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
public void testRewindChange() {
int seconds = UserPreferences.getRewindSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
solo.clickOnText(solo.getString(R.string.pref_rewind));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
assertTrue(currentIndex >= 0 && currentIndex < deltas.length); // found?
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
solo.clickOnButton("Confirm");
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
public void testFastForwardChange() {
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
solo.clickOnText(solo.getString(R.string.pref_fast_forward));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
assertTrue(currentIndex >= 0 && currentIndex < deltas.length); // found?
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
solo.clickOnButton("Confirm");
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
}
}

View File

@ -34,6 +34,16 @@ public class FilenameGeneratorTest extends AndroidTestCase {
createFiles(result);
}
public void testFeedTitleContainsApostrophe() {
String result = FileNameGenerator.generateFileName("Feed's Title ...");
assertEquals("Feeds Title", result);
}
public void testFeedTitleContainsDash() {
String result = FileNameGenerator.generateFileName("Left - Right");
assertEquals("Left Right", result);
}
/**
* Tests if files can be created.
*

View File

@ -0,0 +1,218 @@
package de.danoeh.antennapod.activity;
import android.support.v7.app.AppCompatActivity;
/**
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
* network.
*/
public abstract class CastEnabledActivity extends AppCompatActivity {
// implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
// protected CastManager castManager;
// protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
// private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
//
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
//
// PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
// registerOnSharedPreferenceChangeListener(this);
//
// castManager = CastManager.getInstance();
// castManager.addCastConsumer(castConsumer);
// castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
// onCastConnectionChanged(castManager.isConnected());
// }
//
// @Override
// protected void onDestroy() {
// PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
// .unregisterOnSharedPreferenceChangeListener(this);
// castManager.removeCastConsumer(castConsumer);
// super.onDestroy();
// }
//
// @Override
// @CallSuper
// public boolean onCreateOptionsMenu(Menu menu) {
// super.onCreateOptionsMenu(menu);
// getMenuInflater().inflate(R.menu.cast_enabled, menu);
// castButtonVisibilityManager.setMenu(menu);
// return true;
// }
//
// @Override
// @CallSuper
// public boolean onPrepareOptionsMenu(Menu menu) {
// super.onPrepareOptionsMenu(menu);
// mediaRouteActionProvider = castManager
// .addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
// mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
// return true;
// }
//
// @Override
// protected void onResume() {
// super.onResume();
// castButtonVisibilityManager.setResumed(true);
// }
//
// @Override
// protected void onPause() {
// super.onPause();
// castButtonVisibilityManager.setResumed(false);
// }
//
// @Override
// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
// boolean newValue = UserPreferences.isCastEnabled();
// Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
// castButtonVisibilityManager.setPrefEnabled(newValue);
// // PlaybackService has its own listener, so if it's active we don't have to take action here.
// if (!newValue && !PlaybackService.isRunning) {
// CastManager.getInstance().disconnect();
// }
// }
// }
//
// CastConsumer castConsumer = new DefaultCastConsumer() {
// @Override
// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
// onCastConnectionChanged(true);
// }
//
// @Override
// public void onDisconnected() {
// onCastConnectionChanged(false);
// }
// };
//
// private void onCastConnectionChanged(boolean connected) {
// if (connected) {
// castButtonVisibilityManager.onConnected();
// setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
// } else {
// castButtonVisibilityManager.onDisconnected();
// setVolumeControlStream(AudioManager.STREAM_MUSIC);
// }
// }
//
// /**
// * Should be called by any activity or fragment for which the cast button should be shown.
// *
// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
// */
public final void requestCastButton(int showAsAction) {
// no-op
}
// private class CastButtonVisibilityManager {
// private volatile boolean prefEnabled = false;
// private volatile boolean viewRequested = false;
// private volatile boolean resumed = false;
// private volatile boolean connected = false;
// private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
// private Menu menu;
//
// public synchronized void setPrefEnabled(boolean newValue) {
// if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
// if (newValue) {
// castManager.incrementUiCounter();
// } else {
// castManager.decrementUiCounter();
// }
// }
// prefEnabled = newValue;
// if (mediaRouteActionProvider != null) {
// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
// }
// }
//
// public synchronized void setResumed(boolean newValue) {
// if (resumed == newValue) {
// Log.e(TAG, "resumed should never change to the same value");
// return;
// }
// resumed = newValue;
// if (prefEnabled && (viewRequested || connected)) {
// if (resumed) {
// castManager.incrementUiCounter();
// } else {
// castManager.decrementUiCounter();
// }
// }
// }
//
// public synchronized void setViewRequested(boolean newValue) {
// if (viewRequested != newValue && resumed && prefEnabled && !connected) {
// if (newValue) {
// castManager.incrementUiCounter();
// } else {
// castManager.decrementUiCounter();
// }
// }
// viewRequested = newValue;
// if (mediaRouteActionProvider != null) {
// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
// }
// }
//
// public synchronized void setConnected(boolean newValue) {
// if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
// if (newValue) {
// castManager.incrementUiCounter();
// } else {
// castManager.decrementUiCounter();
// }
// }
// connected = newValue;
// if (mediaRouteActionProvider != null) {
// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
// }
// }
//
// public synchronized boolean shouldEnable() {
// return prefEnabled && viewRequested;
// }
//
// public void setMenu(Menu menu) {
// setViewRequested(false);
// showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
// this.menu = menu;
// setShowAsAction();
// }
//
// public void requestCastButton(int showAsAction) {
// setViewRequested(true);
// this.showAsAction = showAsAction;
// setShowAsAction();
// }
//
// public void onConnected() {
// setConnected(true);
// setShowAsAction();
// }
//
// public void onDisconnected() {
// setConnected(false);
// setShowAsAction();
// }
//
// private void setShowAsAction() {
// if (menu == null) {
// Log.d(TAG, "setShowAsAction() without a menu");
// return;
// }
// MenuItem item = menu.findItem(R.id.media_route_menu_item);
// if (item == null) {
// Log.e(TAG, "setShowAsAction(), but cast button not inflated");
// return;
// }
// MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
// }
// }
}

View File

@ -0,0 +1,7 @@
package de.danoeh.antennapod.config;
import de.danoeh.antennapod.core.CastCallbacks;
public class CastCallbackImpl implements CastCallbacks {
}

View File

@ -0,0 +1,13 @@
package de.danoeh.antennapod.preferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
* Implements functions from PreferenceController that are flavor dependent.
*/
public class PreferenceControllerFlavorHelper {
static void setupFlavoredUI(PreferenceController.PreferenceUI ui) {
ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setEnabled(false);
}
}

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
android:versionCode="1060009"
android:versionName="1.6.0.9">
android:installLocation="auto"
android:versionCode="1060400"
android:versionName="1.6.4.0">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
@ -37,23 +38,35 @@
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"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".activity.MainActivity"
android:configChanges="keyboardHidden|orientation"
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>
<intent-filter>
<action android:name=
"android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name=
"android.intent.category.DEFAULT" />
</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"
@ -85,7 +98,7 @@
<activity
android:name=".activity.PreferenceActivityGingerbread"
android:configChanges="keyboardHidden|orientation"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@ -94,7 +107,7 @@
<activity
android:name=".activity.PreferenceActivity"
android:configChanges="keyboardHidden|orientation"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@ -150,6 +163,7 @@
</activity>
<activity
android:name=".activity.AboutActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/about_pref">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@ -164,12 +178,12 @@
</activity>
<activity
android:name=".activity.OpmlImportFromPathActivity"
android:configChanges="keyboardHidden|orientation"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/opml_import_label">
</activity>
<activity
android:name=".activity.OpmlImportFromIntentActivity"
android:configChanges="keyboardHidden|orientation"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/opml_import_label">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
@ -177,16 +191,30 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="*"
android:pathPattern=".*\\.opml"
android:scheme="file"
android:mimeType="*/*"/>
<data
android:host="*"
android:pathPattern=".*\\.opml"
android:scheme="content"
android:mimeType="*/*"/>
<data android:mimeType="text/xml"/>
<data android:mimeType="text/x-opml"/>
<data android:mimeType="application/xml"/>
<data android:mimeType="application/octet-stream"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:host="*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:mimeType="text/xml"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="text/x-opml"/>
<data android:mimeType="application/xml"/>
<data android:mimeType="application/octet-stream"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
<activity
@ -221,7 +249,7 @@
<activity
android:name=".activity.OnlineFeedViewActivity"
android:configChanges="orientation"
android:configChanges="orientation|screenSize"
android:label="@string/add_feed_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@ -240,6 +268,7 @@
<data android:host="*"/>
<data android:pathPattern=".*\\.xml"/>
<data android:pathPattern=".*\\.rss"/>
<data android:pathPattern=".*\\.atom"/>
</intent-filter>
<!-- Feedburner URLs -->
@ -346,6 +375,10 @@
<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>

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod;
import android.app.Application;
import android.os.Build;
import android.os.StrictMode;
import android.support.multidex.MultiDexApplication;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.fonts.FontAwesomeModule;
@ -13,7 +14,7 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.spa.SPAUtil;
/** Main application class. */
public class PodcastApp extends Application {
public class PodcastApp extends MultiDexApplication {
// make sure that ClientConfigurator executes its static code
static {

View File

@ -124,12 +124,11 @@ public class AboutActivity extends AppCompatActivity {
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(webviewData -> {
webview.loadDataWithBaseURL("file:///android_asset/", webviewData, "text/html",
"utf-8", "about:blank");
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
.subscribe(
webviewData ->
webview.loadDataWithBaseURL("file:///android_asset/", webviewData, "text/html", "utf-8", "about:blank"),
error -> Log.e(TAG, Log.getStackTraceString(error))
);
}
@Override

View File

@ -50,10 +50,10 @@ public class CastplayerActivity extends MediaplayerInfoActivity {
if (butPlaybackSpeed != null) {
butPlaybackSpeed.setVisibility(View.GONE);
}
if (butCastDisconnect != null) {
butCastDisconnect.setOnClickListener(v -> castManager.disconnect());
butCastDisconnect.setVisibility(View.VISIBLE);
}
// if (butCastDisconnect != null) {
// butCastDisconnect.setOnClickListener(v -> castManager.disconnect());
// butCastDisconnect.setVisibility(View.VISIBLE);
// }
}
@Override

View File

@ -89,9 +89,7 @@ public class DirectoryChooserActivity extends AppCompatActivity {
adb.setTitle(R.string.folder_not_empty_dialog_title);
adb.setMessage(R.string.folder_not_empty_dialog_msg);
adb.setNegativeButton(R.string.cancel_label,
(dialog, which) -> {
dialog.dismiss();
});
(dialog, which) -> dialog.dismiss());
adb.setPositiveButton(R.string.confirm_label,
(dialog, which) -> {
dialog.dismiss();
@ -291,9 +289,7 @@ public class DirectoryChooserActivity extends AppCompatActivity {
builder.setMessage(String.format(getString(R.string.create_folder_msg),
CREATE_DIRECTORY_NAME));
builder.setNegativeButton(R.string.cancel_label,
(dialog, which) -> {
dialog.dismiss();
});
(dialog, which) -> dialog.dismiss());
builder.setPositiveButton(R.string.confirm_label,
(dialog, which) -> {
dialog.dismiss();

View File

@ -5,10 +5,10 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
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;
@ -28,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;
@ -41,23 +45,29 @@ 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;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Displays information about a feed.
*/
public class FeedInfoActivity extends ActionBarActivity {
private static final String TAG = "FeedInfoActivity";
private boolean autoDeleteChanged = false;
public class FeedInfoActivity extends AppCompatActivity {
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;
@ -70,6 +80,9 @@ public class FeedInfoActivity extends ActionBarActivity {
private Spinner spnAutoDelete;
private boolean filterInclude = true;
private Subscription subscription;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -91,6 +104,40 @@ public class FeedInfoActivity extends ActionBarActivity {
}
};
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());
@ -102,7 +149,9 @@ public class FeedInfoActivity extends ActionBarActivity {
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);
@ -124,48 +173,62 @@ public class FeedInfoActivity extends ActionBarActivity {
txtvUrl.setOnClickListener(copyUrlToClipboard);
AsyncTask<Long, Void, Feed> loadTask = new AsyncTask<Long, Void, Feed>() {
@Override
protected Feed doInBackground(Long... params) {
return DBReader.getFeed(params[0]);
}
@Override
protected void onPostExecute(Feed result) {
if (result != null) {
subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result == null) {
Log.e(TAG, "Activity was started with invalid arguments");
finish();
}
feed = result;
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
FeedPreferences prefs = feed.getPreferences();
imgvCover.post(() -> Glide.with(FeedInfoActivity.this)
.load(feed.getImageUri())
Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover));
.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);
Iconify.addIcons(txtvUrl);
cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
cbxAutoDownload.setChecked(prefs.getAutoDownload());
cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
feed.getPreferences().setAutoDownload(checked);
feed.savePreferences(FeedInfoActivity.this);
feed.savePreferences();
updateAutoDownloadSettings();
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
feed, checked);
@ -174,7 +237,7 @@ public class FeedInfoActivity extends ActionBarActivity {
cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
feed.getPreferences().setKeepUpdated(checked);
feed.savePreferences(FeedInfoActivity.this);
feed.savePreferences();
});
spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
@ -184,15 +247,12 @@ public class FeedInfoActivity extends ActionBarActivity {
case 0:
auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
break;
case 1:
auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
break;
case 2:
auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
break;
default: // TODO - add exceptions here
return;
}
@ -234,57 +294,12 @@ public class FeedInfoActivity extends ActionBarActivity {
supportInvalidateOptionsMenu();
updateAutoDownloadSettings();
} else {
Log.e(TAG, "Activity was started with invalid arguments");
}
}
};
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, feedId);
} else {
loadTask.execute(feedId);
}
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
});
}
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();
@ -316,6 +331,14 @@ public class FeedInfoActivity extends ActionBarActivity {
}
}
@Override
public void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@ -367,7 +390,7 @@ public class FeedInfoActivity extends ActionBarActivity {
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;

View File

@ -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,10 +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;
@ -148,9 +153,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
final FragmentManager fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(() -> {
drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0);
});
fm.addOnBackStackChangedListener(() -> drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
@ -209,7 +212,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
} else {
edit.remove(PREF_LAST_FRAGMENT_TAG);
}
edit.commit();
edit.apply();
}
private String getLastNavFragment() {
@ -254,9 +257,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
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();
}
@ -466,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) ||
@ -507,21 +509,24 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean retVal = super.onCreateOptionsMenu(menu);
switch (getLastNavFragment()) {
case QueueFragment.TAG:
case EpisodesFragment.TAG:
requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return retVal;
case DownloadsFragment.TAG:
case PlaybackHistoryFragment.TAG:
case AddFeedFragment.TAG:
case SubscriptionFragment.TAG:
return retVal;
default:
requestCastButton(MenuItem.SHOW_AS_ACTION_NEVER);
return retVal;
if (Flavors.FLAVOR == Flavors.PLAY) {
switch (getLastNavFragment()) {
case QueueFragment.TAG:
case EpisodesFragment.TAG:
requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return retVal;
case DownloadsFragment.TAG:
case PlaybackHistoryFragment.TAG:
case AddFeedFragment.TAG:
case SubscriptionFragment.TAG:
return retVal;
default:
requestCastButton(MenuItem.SHOW_AS_ACTION_NEVER);
return retVal;
}
} else {
return retVal;
}
}
@Override
@ -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
@ -585,7 +593,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
};
ConfirmationDialog conDialog = new ConfirmationDialog(this,
R.string.remove_feed_label,
R.string.feed_delete_confirmation_msg) {
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@Override
public void onConfirmButtonPressed(
DialogInterface dialog) {
@ -699,9 +707,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
if (handleIntent) {
handleNavIntent();
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
public void onEvent(QueueEvent event) {
@ -733,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

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
@ -9,6 +10,7 @@ import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
@ -39,8 +41,10 @@ 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.Converter;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.Supplier;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
@ -48,6 +52,8 @@ import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
@ -177,6 +183,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
};
}
protected static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF;
}
protected static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
return activity.txtvRev;
}
protected void onSetSpeedAbilityChanged() {
Log.d(TAG, "onSetSpeedAbilityChanged()");
updatePlaybackSpeedButton();
@ -281,7 +294,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
if (Flavors.FLAVOR == Flavors.PLAY) {
requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mediaplayer, menu);
return true;
@ -294,10 +309,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return false;
}
Playable media = controller.getMedia();
boolean isFeedMedia = media != null && (media instanceof FeedMedia);
menu.findItem(R.id.support_item).setVisible(
media != null && media.getPaymentLink() != null &&
(media instanceof FeedMedia) &&
menu.findItem(R.id.support_item).setVisible(isFeedMedia && media.getPaymentLink() != null &&
((FeedMedia) media).getItem() != null &&
((FeedMedia) media).getItem().getFlattrStatus().flattrable()
);
@ -305,20 +319,21 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null;
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
boolean isItemAndHasLink = media != null && (media instanceof FeedMedia) &&
boolean isItemAndHasLink = isFeedMedia &&
((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null;
menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink);
menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink);
boolean isItemHasDownloadLink = media != null && (media instanceof FeedMedia) && ((FeedMedia) media).getDownload_url() != null;
boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownload_url() != null;
menu.findItem(R.id.share_download_url_item).setVisible(isItemHasDownloadLink);
menu.findItem(R.id.share_download_url_with_position_item).setVisible(isItemHasDownloadLink);
menu.findItem(R.id.share_file).setVisible(isFeedMedia && ((FeedMedia) media).fileExists());
menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
menu.findItem(R.id.add_to_favorites_item).setVisible(false);
menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
if(media != null && media instanceof FeedMedia) {
if (isFeedMedia) {
menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
}
@ -559,6 +574,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
}
break;
case R.id.share_file:
if (media instanceof FeedMedia) {
ShareUtils.shareFeedItemFile(this, ((FeedMedia) media));
}
break;
default:
return false;
}
@ -627,17 +647,16 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
*/
protected boolean loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
if(controller == null || controller.getMedia() == null) {
return false;
}
Playable media = controller.getMedia();
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
if (media != null) {
onPositionObserverUpdate();
checkFavorite();
updatePlaybackSpeedButton();
return true;
} else {
return false;
}
onPositionObserverUpdate();
checkFavorite();
updatePlaybackSpeedButton();
return true;
}
protected void updatePlaybackSpeedButton() {
@ -648,6 +667,91 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
// Only meaningful on AudioplayerActivity, where it is overridden.
}
/**
* Abstract directions to skip forward or back (rewind) and encapsulates behavior to get or set preference (including update of UI on the skip buttons).
*/
static public enum SkipDirection {
SKIP_FORWARD(
UserPreferences::getFastForwardSecs,
MediaplayerActivity::getTxtvFFFromActivity,
UserPreferences::setFastForwardSecs,
R.string.pref_fast_forward),
SKIP_REWIND(UserPreferences::getRewindSecs,
MediaplayerActivity::getTxtvRevFromActivity,
UserPreferences::setRewindSecs,
R.string.pref_rewind);
private final Supplier<Integer> getPrefSecsFn;
private final Func1<MediaplayerActivity, TextView> getTextViewFn;
private final Action1<Integer> setPrefSecsFn;
private final int titleResourceID;
/**
* Constructor for skip direction enum. Stores references to utility functions and resource
* id's that vary dependending on the direction.
*
* @param getPrefSecsFn Handle to function that retrieves current seconds of the skip delta
* @param getTextViewFn Handle to function that gets the TextView which displays the current skip delta value
* @param setPrefSecsFn Handle to function that sets the preference (setting) for the skip delta value (and optionally updates the button label with the current values)
* @param titleResourceID ID of the resource string with the title for a view
*/
SkipDirection(Supplier<Integer> getPrefSecsFn, Func1<MediaplayerActivity, TextView> getTextViewFn, Action1<Integer> setPrefSecsFn, int titleResourceID) {
this.getPrefSecsFn = getPrefSecsFn;
this.getTextViewFn = getTextViewFn;
this.setPrefSecsFn = setPrefSecsFn;
this.titleResourceID = titleResourceID;
}
public int getPrefSkipSeconds() {
return(getPrefSecsFn.get());
}
/**
* Updates preferences for a forward or backward skip depending on the direction of the instance, optionally updating the UI.
*
* @param seconds Number of seconds to set the preference associated with the direction of the instance.
* @param activity MediaplyerActivity that contains textview to update the display of the skip delta setting (or null if nothing to update)
*/
public void setPrefSkipSeconds(int seconds, @Nullable Activity activity) {
setPrefSecsFn.call(seconds);
if (activity != null && activity instanceof MediaplayerActivity) {
TextView tv = getTextViewFn.call((MediaplayerActivity)activity);
if (tv != null) tv.setText(String.valueOf(seconds));
}
}
public int getTitleResourceID() {
return titleResourceID;
}
}
static public void showSkipPreference(Activity activity, SkipDirection direction) {
int checked = 0;
int skipSecs = direction.getPrefSkipSeconds();
final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values);
final String[] choices = new String[values.length];
for (int i = 0; i < values.length; i++) {
if (skipSecs == values[i]) {
checked = i;
}
choices[i] = String.valueOf(values[i]) + " " + activity.getString(R.string.time_seconds);
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(direction.getTitleResourceID());
builder.setSingleChoiceItems(choices, checked, null);
builder.setNegativeButton(R.string.cancel_label, null);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
int choice = ((AlertDialog)dialog).getListView().getCheckedItemPosition();
if (choice < 0 || choice >= values.length) {
System.err.printf("Choice in showSkipPreference is out of bounds %d", choice);
} else {
direction.setPrefSkipSeconds(values[choice], activity);
}
});
builder.create().show();
}
protected void setupGUI() {
setContentView(getContentViewResourceId());
@ -690,7 +794,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
butFF = (ImageButton) findViewById(R.id.butFF);
txtvFF = (TextView) findViewById(R.id.txtvFF);
if (txtvFF != null) {
txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs()));
txtvFF.setText(String.valueOf(UserPreferences.getFastForwardSecs()));
}
butSkip = (ImageButton) findViewById(R.id.butSkip);
@ -702,39 +806,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (butRev != null) {
butRev.setOnClickListener(v -> onRewind());
butRev.setOnLongClickListener(new View.OnLongClickListener() {
int choice;
@Override
public boolean onLongClick(View v) {
int checked = 0;
int rewindSecs = UserPreferences.getRewindSecs();
final int[] values = getResources().getIntArray(R.array.seek_delta_values);
final String[] choices = new String[values.length];
for (int i = 0; i < values.length; i++) {
if (rewindSecs == values[i]) {
checked = i;
}
choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
builder.setTitle(R.string.pref_rewind);
builder.setSingleChoiceItems(choices, checked,
(dialog, which) -> {
choice = values[which];
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
UserPreferences.setPrefRewindSecs(choice);
if(txtvRev != null){
txtvRev.setText(String.valueOf(choice));
}
});
builder.create().show();
return true;
}
butRev.setOnLongClickListener(v -> {
showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_REWIND);
return true;
});
}
@ -742,39 +816,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (butFF != null) {
butFF.setOnClickListener(v -> onFastForward());
butFF.setOnLongClickListener(new View.OnLongClickListener() {
int choice;
@Override
public boolean onLongClick(View v) {
int checked = 0;
int rewindSecs = UserPreferences.getFastFowardSecs();
final int[] values = getResources().getIntArray(R.array.seek_delta_values);
final String[] choices = new String[values.length];
for (int i = 0; i < values.length; i++) {
if (rewindSecs == values[i]) {
checked = i;
}
choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
builder.setTitle(R.string.pref_fast_forward);
builder.setSingleChoiceItems(choices, checked,
(dialog, which) -> {
choice = values[which];
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
UserPreferences.setPrefFastForwardSecs(choice);
if(txtvFF != null) {
txtvFF.setText(String.valueOf(choice));
}
});
builder.create().show();
return true;
}
butFF.setOnLongClickListener(v -> {
showSkipPreference(MediaplayerActivity.this, SkipDirection.SKIP_FORWARD);
return false;
});
}
@ -803,7 +847,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return;
}
int curr = controller.getPosition();
controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000);
controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000);
}
protected abstract int getContentViewResourceId();
@ -865,10 +909,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
isFavorite = isFav;
invalidateOptionsMenu();
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
}
);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}
}

View File

@ -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();
}
@ -277,7 +290,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if (!super.loadMediaInfo()) {
return false;
}
if(controller.getMedia() != media) {
if(controller != null && controller.getMedia() != media) {
media = controller.getMedia();
pagerAdapter.onMediaChanged(media);
}
@ -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
@ -379,7 +395,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
};
ConfirmationDialog conDialog = new ConfirmationDialog(this,
R.string.remove_feed_label,
R.string.feed_delete_confirmation_msg) {
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@Override
public void onConfirmButtonPressed(
DialogInterface dialog) {
@ -443,9 +459,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
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();
}
@ -461,12 +475,20 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if (navAdapter != null) {
navAdapter.notifyDataSetChanged();
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, 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() {

View File

@ -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,22 +101,19 @@ 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(feeds -> {
.subscribe(
feeds -> {
OnlineFeedViewActivity.this.feeds = feeds;
setSubscribeButtonState(feed);
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
}
);
} else if ((arg & EVENTS) != 0) {
@ -130,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());
@ -149,7 +146,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
getSupportActionBar().setTitle(R.string.add_new_feed_label);
getSupportActionBar().setTitle(R.string.add_feed_label);
} else {
throw new IllegalArgumentException("Activity must be started with feedurl argument!");
}
@ -281,30 +278,33 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> {
if (status != null) {
if (!status.isCancelled()) {
if (status.isSuccessful()) {
parseFeed();
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
dialog.show();
}
} else {
String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
if (errorMsg != null && status.getReasonDetailed() != null) {
errorMsg += " (" + status.getReasonDetailed() + ")";
}
showErrorDialog(errorMsg);
}
}
} else {
Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
finish();
}
});
.subscribe(this::checkDownloadResult,
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private void checkDownloadResult(DownloadStatus status) {
if (status == null) {
Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
finish();
}
if (status.isCancelled()) {
return;
}
if (status.isSuccessful()) {
parseFeed();
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
dialog.show();
}
} else {
String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
if (errorMsg != null && status.getReasonDetailed() != null) {
errorMsg += " (" + status.getReasonDetailed() + ")";
}
showErrorDialog(errorMsg);
}
}
private void parseFeed() {
@ -354,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)));
}
}
}
@ -514,9 +519,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
builder.setMessage(R.string.error_msg_prefix);
}
builder.setNeutralButton(android.R.string.ok,
(dialog, which) -> {
dialog.cancel();
}
(dialog, which) -> dialog.cancel()
);
builder.setOnCancelListener(dialog -> {
setResult(RESULT_ERROR);
@ -585,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;
}

View File

@ -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;

View File

@ -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();

View File

@ -1,25 +1,30 @@
package de.danoeh.antennapod.activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/** Lets the user start the OPML-import process. */
/**
* Lets the user start the OPML-import process.
*/
public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
private static final String TAG = "OpmlImportFromIntentAct";
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Uri uri = getIntent().getData();
if(uri.toString().startsWith("/")) {
if (uri != null && uri.toString().startsWith("/")) {
uri = Uri.parse("file://" + uri.toString());
} else {
uri = Uri.parse(getIntent().getStringExtra(Intent.EXTRA_TEXT));
}
importUri(uri);
}

View File

@ -135,7 +135,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == CHOOSE_OPML_FILE) {
Uri uri = data.getData();
if(uri.toString().startsWith("/")) {
if(uri != null && uri.toString().startsWith("/")) {
uri = Uri.parse("file://" + uri.toString());
}
importUri(uri);

View File

@ -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/>

View File

@ -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
@ -103,6 +101,7 @@ public class PreferenceActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
addPreferencesFromResource(R.xml.preferences);
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
@ -118,5 +117,23 @@ public class PreferenceActivity extends AppCompatActivity {
activity.preferenceController.onResume();
}
}
@Override
public void onPause() {
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onPause();
}
super.onPause();
}
@Override
public void onStop() {
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onStop();
}
super.onStop();
}
}
}

View File

@ -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")
@ -54,6 +52,18 @@ public class PreferenceActivityGingerbread extends android.preference.Preference
preferenceController.onResume();
}
@Override
protected void onPause() {
preferenceController.onPause();
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);

View File

@ -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();
}
}

View File

@ -1,14 +1,18 @@
package de.danoeh.antennapod.activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.TextView;
import de.danoeh.antennapod.R;
@ -28,12 +32,16 @@ public class StatisticsActivity extends AppCompatActivity
implements AdapterView.OnItemClickListener {
private static final String TAG = StatisticsActivity.class.getSimpleName();
private static final String PREF_NAME = "StatisticsActivityPrefs";
private static final String PREF_COUNT_ALL = "countAll";
private Subscription subscription;
private TextView totalTimeTextView;
private ListView feedStatisticsList;
private ProgressBar progressBar;
private StatisticsListAdapter listAdapter;
private boolean countAll = false;
private SharedPreferences prefs;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -42,10 +50,14 @@ public class StatisticsActivity extends AppCompatActivity
getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.statistics_activity);
prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
totalTimeTextView = (TextView) findViewById(R.id.total_time);
feedStatisticsList = (ListView) findViewById(R.id.statistics_list);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
listAdapter = new StatisticsListAdapter(this);
listAdapter.setCountAll(countAll);
feedStatisticsList.setAdapter(listAdapter);
feedStatisticsList.setOnItemClickListener(this);
}
@ -53,10 +65,15 @@ public class StatisticsActivity extends AppCompatActivity
@Override
public void onResume() {
super.onResume();
progressBar.setVisibility(View.VISIBLE);
totalTimeTextView.setVisibility(View.GONE);
feedStatisticsList.setVisibility(View.GONE);
loadStats();
refreshStatistics();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.statistics, menu);
return true;
}
@Override
@ -64,30 +81,60 @@ public class StatisticsActivity extends AppCompatActivity
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.statistics_mode) {
selectStatisticsMode();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
private void loadStats() {
if(subscription != null) {
private void selectStatisticsMode() {
View contentView = View.inflate(this, R.layout.statistics_mode_select_dialog, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(contentView);
builder.setTitle(R.string.statistics_mode);
if (countAll) {
((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).setChecked(true);
} else {
((RadioButton) contentView.findViewById(R.id.statistics_mode_normal)).setChecked(true);
}
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
countAll = ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).isChecked();
listAdapter.setCountAll(countAll);
prefs.edit().putBoolean(PREF_COUNT_ALL, countAll).apply();
refreshStatistics();
});
builder.show();
}
private void refreshStatistics() {
progressBar.setVisibility(View.VISIBLE);
totalTimeTextView.setVisibility(View.GONE);
feedStatisticsList.setVisibility(View.GONE);
loadStatistics();
}
private void loadStatistics() {
if (subscription != null) {
subscription.unsubscribe();
}
subscription = Observable.fromCallable(() -> DBReader.getStatistics())
subscription = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
totalTimeTextView.setText(Converter
.shortLocalizedDuration(this, result.totalTime));
.shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
listAdapter.update(result.feedTime);
progressBar.setVisibility(View.GONE);
totalTimeTextView.setVisibility(View.VISIBLE);
feedStatisticsList.setVisibility(View.VISIBLE);
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@Override
@ -97,9 +144,10 @@ public class StatisticsActivity extends AppCompatActivity
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(stats.feed.getTitle());
dialog.setMessage(getString(R.string.statistics_details_dialog,
stats.episodesStarted,
countAll ? stats.episodesStartedIncludingMarked : stats.episodesStarted,
stats.episodes,
Converter.shortLocalizedDuration(this, stats.timePlayed),
Converter.shortLocalizedDuration(this, countAll ?
stats.timePlayedCountAll : stats.timePlayed),
Converter.shortLocalizedDuration(this, stats.time)));
dialog.setPositiveButton(android.R.string.ok, null);
dialog.show();

View File

@ -55,7 +55,7 @@ public class StorageErrorActivity extends AppCompatActivity {
openDirectoryChooser();
}
});
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int readPermission = ActivityCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE);
int writePermission = ActivityCompat.checkSelfPermission(this,

View File

@ -6,11 +6,13 @@ import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
@ -40,7 +42,7 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
* Step 2: Choose device from a list of available devices or create a new one
* Step 3: Choose from a list of actions
*/
public class GpodnetAuthenticationActivity extends ActionBarActivity {
public class GpodnetAuthenticationActivity extends AppCompatActivity {
private static final String TAG = "GpodnetAuthActivity";
private static final String CURRENT_STEP = "current_step";
@ -113,6 +115,9 @@ public class GpodnetAuthenticationActivity extends ActionBarActivity {
final TextView txtvError = (TextView) view.findViewById(R.id.txtvError);
final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progBarLogin);
password.setOnEditorActionListener((v, actionID, event) ->
actionID == EditorInfo.IME_ACTION_GO && login.performClick());
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -131,6 +136,11 @@ public class GpodnetAuthenticationActivity extends ActionBarActivity {
login.setEnabled(false);
progressBar.setVisibility(View.VISIBLE);
txtvError.setVisibility(View.GONE);
// hide the keyboard
InputMethodManager inputManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(login.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}

View File

@ -1,8 +1,9 @@
package de.danoeh.antennapod.adapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.util.LongList;
public interface ActionButtonCallback {
/** Is called when the action button of a list item has been pressed. */
void onActionButtonPressed(FeedItem item);
void onActionButtonPressed(FeedItem item, LongList queueIds);
}

View File

@ -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

View File

@ -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()) {

View File

@ -200,11 +200,11 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(mainActivityRef.get())
.load(item.getImageUri())
.load(item.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover, mainActivityRef.get()));
.into(new CoverTarget(item.getFeed().getImageLocation(), holder.placeholder, holder.cover, mainActivityRef.get()));
}
@Override
@ -232,7 +232,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
actionButtonCallback.onActionButtonPressed(item);
actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds());
}
};
@ -319,6 +319,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
boolean isInQueue(FeedItem item);
LongList getQueueIds();
}
/**

View File

@ -60,6 +60,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
holder.duration = (TextView) convertView.findViewById(R.id.txtvDuration);
holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
convertView.setTag(holder);
} else {
@ -70,6 +71,16 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
holder.title.setText(sc.getTitle());
holder.start.setText(Converter.getDurationStringLong((int) sc
.getStart()));
long duration;
if (position + 1 < media.getChapters().size()) {
duration = media.getChapters().get(position + 1).getStart() - sc.getStart();
} else {
duration = media.getDuration() - sc.getStart();
}
holder.duration.setText(getContext().getString(R.string.chapter_duration,
Converter.getDurationStringLong((int) duration)));
if (sc.getLink() != null) {
holder.link.setVisibility(View.VISIBLE);
holder.link.setText(sc.getLink());
@ -155,6 +166,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
TextView title;
TextView start;
TextView link;
TextView duration;
ImageButton butPlayChapter;
}

View File

@ -18,12 +18,12 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
class CoverTarget extends GlideDrawableImageViewTarget {
private final WeakReference<Uri> fallback;
private final WeakReference<String> fallback;
private final WeakReference<TextView> placeholder;
private final WeakReference<ImageView> cover;
private final WeakReference<MainActivity> mainActivity;
public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover, MainActivity activity) {
public CoverTarget(String fallbackUri, TextView txtvPlaceholder, ImageView imgvCover, MainActivity activity) {
super(imgvCover);
fallback = new WeakReference<>(fallbackUri);
placeholder = new WeakReference<>(txtvPlaceholder);
@ -33,7 +33,7 @@ class CoverTarget extends GlideDrawableImageViewTarget {
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
Uri fallbackUri = fallback.get();
String fallbackUri = fallback.get();
TextView txtvPlaceholder = placeholder.get();
ImageView imgvCover = cover.get();
if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
@ -48,7 +48,7 @@ class CoverTarget extends GlideDrawableImageViewTarget {
}
@Override
public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
public void onResourceReady(GlideDrawable drawable, GlideAnimation<? super GlideDrawable> anim) {
super.onResourceReady(drawable, anim);
TextView txtvPlaceholder = placeholder.get();
if (txtvPlaceholder != null) {

View File

@ -51,13 +51,12 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
@Override
public void onActionButtonPressed(final FeedItem item) {
public void onActionButtonPressed(final FeedItem item, final LongList queueIds) {
if (item.hasMedia()) {
final FeedMedia media = item.getMedia();
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!isDownloading && !media.isDownloaded()) {
LongList queueIds = DBReader.getQueueIDList();
if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
try {
DBTasks.downloadFeedItems(context, item);

View File

@ -12,6 +12,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@ -77,7 +78,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
}
Glide.with(context)
.load(item.getImageUri())
.load(item.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
@ -85,6 +86,12 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
.dontAnimate()
.into(holder.imageView);
if(item.isPlayed()) {
ViewHelper.setAlpha(convertView, 0.5f);
} else {
ViewHelper.setAlpha(convertView, 1.0f);
}
holder.title.setText(item.getTitle());
holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
holder.queueStatus.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);

View File

@ -26,6 +26,7 @@ import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ThemeUtils;
/**
@ -86,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);
@ -219,7 +221,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
callback.onActionButtonPressed(item);
callback.onActionButtonPressed(item, itemAccess.getQueueIds());
}
};
@ -243,6 +245,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedItem getItem(int position);
LongList getQueueIds();
}
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
@ -21,6 +22,7 @@ import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -45,6 +47,7 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
*/
public class NavListAdapter extends BaseAdapter
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final int VIEW_TYPE_COUNT = 3;
public static final int VIEW_TYPE_NAV = 0;
public static final int VIEW_TYPE_SECTION_DIVIDER = 1;
@ -60,12 +63,12 @@ public class NavListAdapter extends BaseAdapter
private static String[] titles;
private ItemAccess itemAccess;
private Context context;
private WeakReference<Activity> activity;
private boolean showSubscriptionList = true;
public NavListAdapter(ItemAccess itemAccess, Context context) {
public NavListAdapter(ItemAccess itemAccess, Activity context) {
this.itemAccess = itemAccess;
this.context = context;
this.activity = new WeakReference<>(context);
titles = context.getResources().getStringArray(R.array.nav_drawer_titles);
loadItems();
@ -108,6 +111,10 @@ public class NavListAdapter extends BaseAdapter
}
private Drawable getDrawable(String tag) {
Activity context = activity.get();
if(context == null) {
return null;
}
int icon;
switch (tag) {
case QueueFragment.TAG:
@ -218,6 +225,10 @@ public class NavListAdapter extends BaseAdapter
}
private View getNavView(String title, int position, View convertView, ViewGroup parent) {
Activity context = activity.get();
if(context == null) {
return null;
}
NavHolder holder;
if (convertView == null) {
holder = new NavHolder();
@ -236,30 +247,28 @@ public class NavListAdapter extends BaseAdapter
holder.title.setText(title);
// reset for re-use
holder.count.setVisibility(View.GONE);
holder.count.setOnClickListener(null);
String tag = tags.get(position);
if (tag.equals(QueueFragment.TAG)) {
int queueSize = itemAccess.getQueueSize();
if (queueSize > 0) {
holder.count.setVisibility(View.VISIBLE);
holder.count.setText(String.valueOf(queueSize));
} else {
holder.count.setVisibility(View.GONE);
holder.count.setVisibility(View.VISIBLE);
}
} else if (tag.equals(EpisodesFragment.TAG)) {
int unreadItems = itemAccess.getNumberOfNewItems();
if (unreadItems > 0) {
holder.count.setVisibility(View.VISIBLE);
holder.count.setText(String.valueOf(unreadItems));
} else {
holder.count.setVisibility(View.GONE);
holder.count.setVisibility(View.VISIBLE);
}
} else if (tag.equals(SubscriptionFragment.TAG)) {
int sum = itemAccess.getFeedCounterSum();
if (sum > 0) {
holder.count.setVisibility(View.VISIBLE);
holder.count.setText(String.valueOf(sum));
} else {
holder.count.setVisibility(View.GONE);
holder.count.setVisibility(View.VISIBLE);
}
} else if(tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
int epCacheSize = UserPreferences.getEpisodeCacheSize();
@ -278,11 +287,7 @@ public class NavListAdapter extends BaseAdapter
.setPositiveButton(android.R.string.ok, (dialog, which) -> {})
.show()
);
} else {
holder.count.setVisibility(View.GONE);
}
} else {
holder.count.setVisibility(View.GONE);
}
holder.image.setImageDrawable(getDrawable(tags.get(position)));
@ -291,6 +296,10 @@ public class NavListAdapter extends BaseAdapter
}
private View getSectionDividerView(View convertView, ViewGroup parent) {
Activity context = activity.get();
if(context == null) {
return null;
}
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@ -303,6 +312,10 @@ public class NavListAdapter extends BaseAdapter
}
private View getFeedView(int position, View convertView, ViewGroup parent) {
Activity context = activity.get();
if(context == null) {
return null;
}
int feedPos = position - getSubscriptionOffset();
Feed feed = itemAccess.getItem(feedPos);
@ -324,7 +337,7 @@ public class NavListAdapter extends BaseAdapter
}
Glide.with(context)
.load(feed.getImageUri())
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)

View File

@ -296,11 +296,11 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(mainActivity.get())
.load(item.getImageUri())
.load(item.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageUri(), placeholder, cover, mainActivity.get()));
.into(new CoverTarget(item.getFeed().getImageLocation(), placeholder, cover, mainActivity.get()));
}
}
@ -309,7 +309,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
actionButtonCallback.onActionButtonPressed(item);
actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds());
}
};

View File

@ -11,6 +11,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
@ -80,7 +81,7 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setVisibility(View.GONE);
Glide.with(context)
.load(feed.getImageUri())
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
@ -96,8 +97,10 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle());
}
ViewHelper.setAlpha(convertView, item.isPlayed() ? 0.5f : 1.0f);
Glide.with(context)
.load(item.getFeed().getImageUri())
.load(item.getFeed().getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)

View File

@ -9,11 +9,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.widget.IconTextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
@ -27,11 +25,15 @@ import de.danoeh.antennapod.core.util.Converter;
public class StatisticsListAdapter extends BaseAdapter {
private Context context;
List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
private boolean countAll = true;
public StatisticsListAdapter(Context context) {
this.context = context;
}
public void setCountAll(boolean countAll) {
this.countAll = countAll;
}
@Override
public int getCount() {
@ -69,7 +71,7 @@ public class StatisticsListAdapter extends BaseAdapter {
}
Glide.with(context)
.load(feed.getImageUri())
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
@ -79,7 +81,8 @@ public class StatisticsListAdapter extends BaseAdapter {
holder.title.setText(feed.getTitle());
holder.time.setText(Converter.shortLocalizedDuration(context,
feedTime.get(position).timePlayed));
countAll ? feedTime.get(position).timePlayedCountAll
: feedTime.get(position).timePlayed));
return convertView;
}

View File

@ -1,9 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.net.Uri;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -32,8 +30,8 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
/** placeholder object that indicates item should be added */
public static final Object ADD_ITEM_OBJ = new Object();
/** the position in the view that holds the add item */
private static final int ADD_POSITION = 0;
/** the position in the view that holds the add item; 0 is the first, -1 is the last position */
private static final int ADD_POSITION = -1;
private static final String TAG = "SubscriptionsAdapter";
private final WeakReference<MainActivity> mainActivityRef;
@ -44,8 +42,16 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
this.itemAccess = itemAccess;
}
private int getAddTilePosition() {
if(ADD_POSITION < 0) {
return ADD_POSITION + getCount();
}
return ADD_POSITION;
}
private int getAdjustedPosition(int origPosition) {
return origPosition - 1;
assert(origPosition != getAddTilePosition());
return origPosition < getAddTilePosition() ? origPosition : origPosition - 1;
}
@Override
@ -55,15 +61,20 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
@Override
public Object getItem(int position) {
if (position == ADD_POSITION) {
if (position == getAddTilePosition()) {
return ADD_ITEM_OBJ;
}
return itemAccess.getItem(getAdjustedPosition(position));
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public long getItemId(int position) {
if (position == ADD_POSITION) {
if (position == getAddTilePosition()) {
return 0;
}
return itemAccess.getItem(getAdjustedPosition(position)).getId();
@ -89,13 +100,17 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder = (Holder) convertView.getTag();
}
if (position == ADD_POSITION) {
if (position == getAddTilePosition()) {
holder.feedTitle.setText("{md-add 500%}\n\n" + mainActivityRef.get().getString(R.string.add_feed_label));
holder.feedTitle.setVisibility(View.VISIBLE);
// prevent any accidental re-use of old values (not sure how that would happen...)
holder.count.setPrimaryText("");
// make it go away, we don't need it for add feed
holder.count.setVisibility(View.INVISIBLE);
// when this holder is reused, we could else end up with a cover image
Glide.clear(holder.imageView);
return convertView;
}
@ -104,10 +119,15 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder.feedTitle.setText(feed.getTitle());
holder.feedTitle.setVisibility(View.VISIBLE);
holder.count.setPrimaryText(String.valueOf(itemAccess.getFeedCounter(feed.getId())));
holder.count.setVisibility(View.VISIBLE);
int count = itemAccess.getFeedCounter(feed.getId());
if(count > 0) {
holder.count.setPrimaryText(String.valueOf(itemAccess.getFeedCounter(feed.getId())));
holder.count.setVisibility(View.VISIBLE);
} else {
holder.count.setVisibility(View.GONE);
}
Glide.with(mainActivityRef.get())
.load(feed.getImageUri())
.load(feed.getImageLocation())
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
@ -119,7 +139,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == ADD_POSITION) {
if (position == getAddTilePosition()) {
mainActivityRef.get().loadChildFragment(new AddFeedFragment());
} else {
Fragment fragment = ItemlistFragment.newInstance(getItemId(position));

View File

@ -1,6 +1,8 @@
package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
@ -18,6 +20,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 +45,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);
@ -66,7 +70,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
//Set the title
viewHolder.titleView.setText(podcast.title);
if(!podcast.feedUrl.contains("itunes.apple.com")) {
if(podcast.feedUrl != null && !podcast.feedUrl.contains("itunes.apple.com")) {
viewHolder.urlView.setText(podcast.feedUrl);
viewHolder.urlView.setVisibility(View.VISIBLE);
} else {
@ -86,35 +90,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.
*/
@ -128,14 +103,16 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
/**
* URL of the podcast image
*/
@Nullable
public final String imageUrl;
/**
* URL of the podcast feed
*/
@Nullable
public final String feedUrl;
private Podcast(String title, String imageUrl, String feedUrl) {
private Podcast(String title, @Nullable String imageUrl, @Nullable String feedUrl) {
this.title = title;
this.imageUrl = imageUrl;
this.feedUrl = feedUrl;
@ -148,12 +125,16 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @throws JSONException
*/
public static Podcast fromSearch(JSONObject json) throws JSONException {
String title = json.getString("collectionName");
String imageUrl = json.getString("artworkUrl100");
String feedUrl = json.getString("feedUrl");
String title = json.optString("collectionName", "");
String imageUrl = json.optString("artworkUrl100", null);
String feedUrl = json.optString("feedUrl", null);
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 +158,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);
}
}
}

View File

@ -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();
}
});
}
}

View File

@ -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();
}
}
}

View File

@ -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;

View File

@ -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>> {
@ -75,9 +75,7 @@ public class OpmlImportWorker extends
alert.setTitle(R.string.error_label);
alert.setMessage(context.getString(R.string.opml_reader_error)
+ exception.getMessage());
alert.setNeutralButton(android.R.string.ok, (dialog, which) -> {
dialog.dismiss();
});
alert.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
alert.create().show();
}
}

View File

@ -18,5 +18,6 @@ public class ClientConfigurator {
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
ClientConfig.castCallbacks = new CastCallbackImpl();
}
}

View File

@ -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) {

View File

@ -35,9 +35,7 @@ public class GpodnetSetHostnameDialog {
}
dialog1.dismiss();
})
.setNegativeButton(R.string.cancel_label, (dialog1, which) -> {
dialog1.cancel();
})
.setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel())
.setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, (dialog1, which) -> {
GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
dialog1.dismiss();

View File

@ -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 -> {

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -95,9 +95,7 @@ public class VariableSpeedDialog {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.set_playback_speed_label);
builder.setMultiChoiceItems(R.array.playback_speed_values,
speedChecked, (dialog, which, isChecked) -> {
speedChecked[which] = isChecked;
});
speedChecked, (dialog, which, isChecked) -> speedChecked[which] = isChecked);
builder.setNegativeButton(android.R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok,
(dialog, which) -> {

View File

@ -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)));

View File

@ -80,6 +80,7 @@ public class AllEpisodesFragment extends Fragment {
private boolean viewsCreated = false;
private boolean isUpdatingFeeds;
protected boolean isMenuInvalidationAllowed = false;
protected Subscription subscription;
private LinearLayoutManager layoutManager;
@ -180,7 +181,7 @@ public class AllEpisodesFragment extends Fragment {
}
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
inflater.inflate(R.menu.new_episodes, menu);
inflater.inflate(R.menu.episodes, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
@ -206,11 +207,13 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (itemsLoaded) {
MenuItem menuItem = menu.findItem(R.id.mark_all_read_item);
if (menuItem != null) {
menuItem.setVisible(episodes != null && !episodes.isEmpty());
}
MenuItem markAllRead = menu.findItem(R.id.mark_all_read_item);
if (markAllRead != null) {
markAllRead.setVisible(!showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
}
MenuItem markAllSeen = menu.findItem(R.id.mark_all_seen_item);
if(markAllSeen != null) {
markAllSeen.setVisible(showOnlyNewEpisodes() && episodes != null && !episodes.isEmpty());
}
}
@ -225,19 +228,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:
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;
@ -378,6 +394,20 @@ public class AllEpisodesFragment extends Fragment {
return item != null && item.isTagged(FeedItem.TAG_QUEUE);
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
if(episodes == null) {
return queueIds;
}
for(FeedItem item : episodes) {
if(item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
};
public void onEventMainThread(FeedItemEvent event) {
@ -401,7 +431,7 @@ public class AllEpisodesFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (isUpdatingFeeds != update.feedIds.length > 0) {
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
getActivity().supportInvalidateOptionsMenu();
}
if(listAdapter != null && update.mediaIds.length > 0) {
@ -450,9 +480,7 @@ public class AllEpisodesFragment extends Fragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
protected List<FeedItem> loadData() {

View File

@ -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);
}
}
}

View File

@ -215,9 +215,7 @@ public class CompletedDownloadsFragment extends ListFragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}

View File

@ -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");
}
}
@ -67,7 +60,7 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
txtvPodcastTitle.setText(media.getFeedTitle());
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
.load(media.getImageUri())
.load(media.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
.fitCenter()
@ -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();
}
}
}

View File

@ -165,9 +165,7 @@ public class DownloadLogFragment extends ListFragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}

View File

@ -79,6 +79,11 @@ public class EpisodesFragment extends Fragment {
public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
private final Resources resources;
private AllEpisodesFragment[] fragments = {
new NewEpisodesFragment(),
new AllEpisodesFragment(),
new FavoriteEpisodesFragment()
};
public EpisodesPagerAdapter(FragmentManager fm, Resources resources) {
super(fm);
@ -87,15 +92,7 @@ public class EpisodesFragment extends Fragment {
@Override
public Fragment getItem(int position) {
switch (position) {
case POS_ALL_EPISODES:
return new AllEpisodesFragment();
case POS_NEW_EPISODES:
return new NewEpisodesFragment();
case POS_FAV_EPISODES:
return new FavoriteEpisodesFragment();
}
return null;
return fragments[position];
}
@Override
@ -116,5 +113,14 @@ public class EpisodesFragment extends Fragment {
return super.getPageTitle(position);
}
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
for (int i = 0; i < TOTAL_COUNT; i++) {
// Invalidating the OptionsMenu is only allowed for the currently active fragment
fragments[i].isMenuInvalidationAllowed = (i == position);
}
}
}
}

View File

@ -163,7 +163,7 @@ public class ExternalPlayerFragment extends Fragment {
((double) controller.getPosition() / controller.getDuration() * 100));
Glide.with(getActivity())
.load(media.getImageUri())
.load(media.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)

View File

@ -71,9 +71,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item),
Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.addFavoriteItem(item);
});
snackbar.setAction(getString(R.string.undo), v -> DBWriter.addFavoriteItem(item));
snackbar.show();
}
}

View File

@ -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);
}
}

View File

@ -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);
@ -196,9 +198,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
.subscribe(feedItem -> {
shownotesProvider = feedItem;
load();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}
@ -311,9 +311,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
webvDescription.loadDataWithBaseURL(null, data, "text/html",
"utf-8", "about:blank");
Log.d(TAG, "Webview loaded");
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private String loadData() {
@ -348,7 +346,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
private boolean restoreFromPreference() {
if (!saveState) {
if (saveState) {
Log.d(TAG, "Restoring from preferences");
Activity activity = getActivity();
if (activity != null) {
@ -381,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();
}
}
}

View File

@ -44,9 +44,7 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@ -60,7 +58,9 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@ -222,7 +222,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
return;
}
DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
actionButtonCallback.onActionButtonPressed(item);
actionButtonCallback.onActionButtonPressed(item, item.isTagged(FeedItem.TAG_QUEUE) ?
LongList.of(item.getId()) : new LongList(0));
FeedMedia media = item.getMedia();
if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
@ -312,7 +313,10 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if(!isAdded() || item == null) {
return;
}
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
super.onCreateOptionsMenu(menu, inflater);
if (Flavors.FLAVOR == Flavors.PLAY) {
((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {
@ -374,7 +378,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
Glide.with(getActivity())
.load(item.getImageUri())
.load(item.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
@ -417,7 +421,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
butAction2Text = R.string.stream_label;
} else {
butAction2Icon = "{md-delete 24sp}";
butAction2Text = R.string.remove_label;
butAction2Text = R.string.delete_label;
}
if (isDownloading) {
butAction1Icon = "{md-cancel 24sp}";
@ -576,9 +580,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
item = result;
itemsLoaded = true;
onFragmentLoaded();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private FeedItem loadInBackground() {

View File

@ -64,8 +64,10 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
@ -98,6 +100,7 @@ public class ItemlistFragment extends ListFragment {
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean headerCreated = false;
private List<Downloader> downloaderList;
@ -105,7 +108,10 @@ public class ItemlistFragment extends ListFragment {
private boolean isUpdatingFeed;
private TextView txtvTitle;
private IconTextView txtvFailure;
private ImageView imgvBackground;
private ImageView imgvCover;
private TextView txtvInformation;
@ -247,6 +253,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) {
@ -258,7 +267,7 @@ public class ItemlistFragment extends ListFragment {
};
ConfirmationDialog conDialog = new ConfirmationDialog(getActivity(),
R.string.remove_feed_label,
R.string.feed_delete_confirmation_msg) {
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@Override
public void onConfirmButtonPressed(
@ -414,6 +423,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();
}
@ -459,15 +469,17 @@ 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;
}
loadFeedImage();
if(feed.hasLastUpdateFailed()) {
txtvFailure.setVisibility(View.VISIBLE);
} else {
txtvFailure.setVisibility(View.GONE);
}
txtvTitle.setText(feed.getTitle());
if(feed.getItemFilter() != null) {
FeedItemFilter filter = feed.getItemFilter();
if(filter.getValues().length > 0) {
@ -497,10 +509,10 @@ 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);
imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground);
imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo);
txtvInformation = (TextView) header.findViewById(R.id.txtvInformation);
txtvFailure = (IconTextView) header.findViewById(R.id.txtvFailure);
@ -512,23 +524,7 @@ public class ItemlistFragment extends ListFragment {
// https://github.com/bumptech/glide/issues/529
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
Glide.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation(getActivity()))
.dontAnimate()
.into(imgvBackground);
Glide.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover);
loadFeedImage();
butShowInfo.setOnClickListener(v -> {
if (viewsCreated && itemsLoaded) {
@ -538,6 +534,27 @@ public class ItemlistFragment extends ListFragment {
startActivity(startIntent);
}
});
headerCreated = true;
}
private void loadFeedImage() {
Glide.with(getActivity())
.load(feed.getImageLocation())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation(getActivity()))
.dontAnimate()
.into(imgvBackground);
Glide.with(getActivity())
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover);
}
@ -577,6 +594,20 @@ public class ItemlistFragment extends ListFragment {
}
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
if(feed == null) {
return queueIds;
}
for(FeedItem item : feed.getItems()) {
if(item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
@Override
public int getCount() {
return (feed != null) ? feed.getNumOfItems() : 0;
@ -612,9 +643,7 @@ public class ItemlistFragment extends ListFragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private Feed loadData() {

View File

@ -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;

View File

@ -7,6 +7,8 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -92,7 +94,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
}
};
Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label),
Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_seen_label),
Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());

View File

@ -28,6 +28,7 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
@ -251,6 +252,20 @@ public class PlaybackHistoryFragment extends ListFragment {
return null;
}
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
if(playbackHistory == null) {
return queueIds;
}
for (FeedItem item : playbackHistory) {
if (item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
};
private void loadItems() {
@ -268,9 +283,7 @@ public class PlaybackHistoryFragment extends ListFragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private List<FeedItem> loadData() {

View File

@ -304,11 +304,11 @@ public class QueueFragment extends Fragment {
};
conDialog.createNewDialog().show();
return true;
case R.id.queue_sort_alpha_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.ALPHA_ASC, true);
case R.id.queue_sort_episode_title_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_ASC, true);
return true;
case R.id.queue_sort_alpha_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.ALPHA_DESC, true);
case R.id.queue_sort_episode_title_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.EPISODE_TITLE_DESC, true);
return true;
case R.id.queue_sort_date_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_ASC, true);
@ -322,6 +322,12 @@ public class QueueFragment extends Fragment {
case R.id.queue_sort_duration_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_DESC, true);
return true;
case R.id.queue_sort_feed_title_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_ASC, true);
return true;
case R.id.queue_sort_feed_title_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true);
return true;
default:
return false;
}
@ -605,9 +611,7 @@ public class QueueFragment extends Fragment {
recyclerAdapter.notifyDataSetChanged();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
}

View File

@ -182,6 +182,9 @@ public class SearchFragment extends ListFragment {
}
searchAdapter.notifyDataSetChanged();
setListShown(true);
String query = getArguments().getString(ARG_QUERY);
setEmptyText(getString(R.string.no_results_for_query, query));
}
private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() {
@ -219,9 +222,7 @@ public class SearchFragment extends ListFragment {
onFragmentLoaded();
}
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private List<SearchResult> performSearch() {

View File

@ -26,7 +26,9 @@ 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;
import rx.schedulers.Schedulers;
@ -46,6 +48,7 @@ public class SubscriptionFragment extends Fragment {
private int mPosition = -1;
private Subscription subscription;
public SubscriptionFragment() {
}
@ -88,16 +91,25 @@ public class SubscriptionFragment extends Fragment {
EventDistributor.getInstance().register(contentUpdate);
}
@Override
public void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
}
}
private void loadSubscriptions() {
Observable.fromCallable(() -> DBReader.getNavDrawerData())
if(subscription != null) {
subscription.unsubscribe();
}
subscription = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
subscriptionAdapter.notifyDataSetChanged();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@Override
@ -143,21 +155,18 @@ public class SubscriptionFragment extends Fragment {
Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
loadSubscriptions();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
.subscribe(result -> loadSubscriptions(),
error -> Log.e(TAG, Log.getStackTraceString(error)));
return true;
case R.id.mark_all_read_item:
Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
loadSubscriptions();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
});
.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) {
@ -169,7 +178,7 @@ public class SubscriptionFragment extends Fragment {
};
ConfirmationDialog conDialog = new ConfirmationDialog(getContext(),
R.string.remove_feed_label,
R.string.feed_delete_confirmation_msg) {
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@Override
public void onConfirmButtonPressed(
DialogInterface dialog) {

View File

@ -80,7 +80,7 @@ public class FeedItemMenuHandler {
if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) {
mi.setItemVisibility(R.id.move_to_bottom_item, false);
}
if (!isInQueue || isPlaying) {
if (!isInQueue) {
mi.setItemVisibility(R.id.remove_from_queue_item, false);
}
if (!(!isInQueue && selectedItem.getMedia() != null)) {
@ -101,6 +101,8 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
mi.setItemVisibility(R.id.share_file, hasMedia && selectedItem.getMedia().fileExists());
if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
} else {
@ -239,6 +241,9 @@ public class FeedItemMenuHandler {
case R.id.share_download_url_with_position_item:
ShareUtils.shareFeedItemDownloadLink(context, selectedItem, true);
break;
case R.id.share_file:
ShareUtils.shareFeedItemFile(context, selectedItem.getMedia());
break;
default:
Log.d(TAG, "Unknown menuItemId: " + menuItemId);
return false;

View File

@ -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 };

View File

@ -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;
@ -28,14 +29,13 @@ import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
import android.widget.EditText;
import android.widget.Toast;
import android.widget.ListView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import org.apache.commons.lang3.ArrayUtils;
@ -43,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;
@ -52,10 +54,14 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.MainActivity;
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,35 +86,45 @@ 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";
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_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_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";
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;
private final PreferenceUI ui;
private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener =
(sharedPreferences, key) -> {
if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) {
updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
GpodnetPreferences.getLastSyncAttemptTimestamp());
}
};
private CheckBoxPreference[] selectedNetworks;
private Subscription subscription;
public PreferenceController(PreferenceUI ui) {
this.ui = ui;
@ -112,6 +132,19 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
.registerOnSharedPreferenceChangeListener(this);
}
/**
* Returns the preference activity that should be used on this device.
*
* @return PreferenceActivity if the API level is greater than 10, PreferenceActivityGingerbread otherwise.
*/
public static Class<? extends Activity> getPreferenceActivity() {
if (Build.VERSION.SDK_INT > 10) {
return PreferenceActivity.class;
} else {
return PreferenceActivityGingerbread.class;
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(key.equals(UserPreferences.PREF_SONIC)) {
@ -122,19 +155,6 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
/**
* Returns the preference activity that should be used on this device.
*
* @return PreferenceActivity if the API level is greater than 10, PreferenceActivityGingerbread otherwise.
*/
public static Class getPreferenceActivity() {
if (Build.VERSION.SDK_INT > 10) {
return PreferenceActivity.class;
} else {
return PreferenceActivityGingerbread.class;
}
}
public void onCreate() {
final Activity activity = ui.getActivity();
@ -170,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 &&
@ -318,6 +336,16 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
VariableSpeedDialog.showDialog(activity);
return true;
});
ui.findPreference(PreferenceController.PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
.setOnPreferenceClickListener(preference -> {
MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
return true;
});
ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
.setOnPreferenceClickListener(preference -> {
MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
return true;
});
ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
.setOnPreferenceClickListener(preference -> {
AuthenticationDialog dialog = new AuthenticationDialog(activity,
@ -340,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();
@ -412,28 +452,47 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
return true;
});
//checks whether Google Play Services is installed on the device (condition necessary for Cast support)
ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setOnPreferenceChangeListener((preference, o) -> {
if (o instanceof Boolean && ((Boolean) o)) {
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(ui.getActivity());
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
return true;
} else {
GoogleApiAvailability.getInstance()
.getErrorDialog(ui.getActivity(), googlePlayServicesCheck, 0)
.show();
return false;
}
}
return true;
});
PreferenceControllerFlavorHelper.setupFlavoredUI(ui);
buildEpisodeCleanupPreference();
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
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));
@ -450,9 +509,20 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
setParallelDownloadsText(UserPreferences.getParallelDownloads());
setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
setDataFolderText();
GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
updateGpodnetPreferenceScreen();
}
public void onPause() {
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 &&
@ -494,18 +564,41 @@ 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) {
String format = ui.getActivity().getString(R.string.pref_gpodnet_login_status);
String summary = String.format(format, GpodnetPreferences.getUsername(),
GpodnetPreferences.getDeviceID());
ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
GpodnetPreferences.getLastSyncAttemptTimestamp());
} else {
ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(null);
updateLastGpodnetSyncReport(false, 0);
}
ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
}
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_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 :
R.string.gpodnetsync_pref_report_failed),
DateUtils.getRelativeDateTimeString(ui.getActivity(),
lastTime,
DateUtils.MINUTE_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,
DateUtils.FORMAT_SHOW_TIME)));
} else {
sync.setSummary(ui.getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
}
}
private String[] getUpdateIntervalEntries(final String[] values) {
final Resources res = ui.getActivity().getResources();
String[] entries = new String[values.length];
@ -621,7 +714,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
timeOfDayStr);
} else {
val = context.getString(R.string.pref_smart_mark_as_played_disabled);
val = context.getString(R.string.pref_smart_mark_as_played_disabled); // TODO: Is this a bug? Otherwise document why is this related to smart mark???
}
}
String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
@ -666,10 +759,16 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
clearAutodownloadSelectedNetworsPreference();
}
// get configured networks
WifiManager wifiservice = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
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());
@ -754,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();
}
@ -800,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();
}

View File

@ -1,5 +1,3 @@
Easy-to-use, flexible and open-source podcast manager and player
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
@ -35,6 +33,9 @@ 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

View File

@ -1,5 +1,3 @@
Easy-to-use, flexible and open-source podcast manager and player
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
@ -35,6 +33,9 @@ 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

View File

@ -1,5 +1,3 @@
Easy-to-use, flexible and open-source podcast manager and player
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
@ -35,6 +33,9 @@ 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

View File

@ -1,5 +1,3 @@
Easy-to-use, flexible and open-source podcast manager and player
AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based your favourites and delay settings).<br>
But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer. You can even show your love to the content creators with our Flattr integration.
@ -35,6 +33,9 @@ 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

View File

@ -0,0 +1 @@
Easy-to-use, flexible and open-source podcast manager and player

View File

@ -0,0 +1 @@
AntennaPod

View File

View File

@ -0,0 +1,11 @@
* New features:
* Subscription overview
* Proxy support
* Statistics
* Manual gpodder.net sync
* Fixes:
* Audioplayer controls
* Audio ducking
* Video control fade-out
* External media controls
* Feed parsing

View File

@ -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>
&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
&#8226; Access password-protected feeds and episodes<br>
&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
KEEP TRACK, SHARE & APPRECIATE<br>
&#8226; Keep track of the best of the best by marking episodes as favourites<br>
&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
&#8226; Support content creators with Flattr integration including automatic flattring
CONTROL THE SYSTEM<br>
&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
&#8226; Adapt to your environment using the light and dark theme<br>
&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
<b>Join the AntennaPod community!</b><br>
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
GitHub is the place to go for feature requests, bug reports and code contributions:<br>
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

View File

@ -0,0 +1 @@
antennapod@googlegroups.com

Some files were not shown because too many files have changed in this diff Show More