Merge branch 'develop'

This commit is contained in:
Martin Fietz 2018-10-20 21:55:44 +02:00
commit 12c5819380
547 changed files with 11262 additions and 6904 deletions

36
.circleci/config.yml Normal file
View File

@ -0,0 +1,36 @@
version: 2
jobs:
build:
docker:
- image: circleci/android:api-26-alpha
working_directory: ~/AntennaPod
environment:
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
steps:
- checkout
- restore_cache:
keys:
- v1-android-{{ checksum "build.gradle" }}
# fallback to using the latest cache if no exact match is found
- v1-android-
- run:
command: ./gradlew assembleDebug :core:testPlayDebugUnitTest -PdisablePreDex
no_output_timeout: 1800
- store_artifacts:
path: app/build/outputs/apk
destination: apks
- save_cache:
paths:
- ~/.android
- ~/.gradle
- ~/android
key: v1-android-{{ checksum "build.gradle" }}

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AntennaPod</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -1,17 +0,0 @@
language: android
jdk: oraclejdk7
env:
matrix:
- ANDROID_SDKS=android-19,sysimg-19 ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
before_install:
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
- emulator -avd test -no-skin -no-audio -no-window &
- cp src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example src/de/danoeh/antennapod/util/flattr/FlattrConfig.java
before_script:
- chmod -R 777 ./ci/wait_for_emulator.sh
- ./ci/wait_for_emulator.sh
script:
- gradle connectedAndroidTest

View File

@ -7,6 +7,7 @@ source_lang = en
trans.ast_ES = core/src/main/res/values-b+ast/strings.xml trans.ast_ES = core/src/main/res/values-b+ast/strings.xml
trans.ar = core/src/main/res/values-ar/strings.xml trans.ar = core/src/main/res/values-ar/strings.xml
trans.az = core/src/main/res/values-az/strings.xml trans.az = core/src/main/res/values-az/strings.xml
trans.bg = core/src/main/res/values-bg/strings.xml
trans.ca = core/src/main/res/values-ca/strings.xml trans.ca = core/src/main/res/values-ca/strings.xml
trans.ca_ES = core/src/main/res/values-ca-rES/strings.xml trans.ca_ES = core/src/main/res/values-ca-rES/strings.xml
trans.cs_CZ = core/src/main/res/values-cs-rCZ/strings.xml trans.cs_CZ = core/src/main/res/values-cs-rCZ/strings.xml
@ -32,6 +33,7 @@ trans.kn_IN = core/src/main/res/values-kn-rIN/strings.xml
trans.ko = core/src/main/res/values-ko/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.ko_KR = core/src/main/res/values-ko-rKR/strings.xml
trans.lt = core/src/main/res/values-lt/strings.xml trans.lt = core/src/main/res/values-lt/strings.xml
trans.mk = core/src/main/res/values-mk/strings.xml
trans.nb = core/src/main/res/values-nb/strings.xml trans.nb = core/src/main/res/values-nb/strings.xml
trans.no = core/src/main/res/values-no/strings.xml trans.no = core/src/main/res/values-no/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml trans.nl = core/src/main/res/values-nl/strings.xml

View File

@ -1,6 +1,32 @@
Change Log Change Log
========== ==========
Version 1.7.0
-------------
* NEW ExoPlayer (experimental)
* Fix for Bluetooth Forward (Oreo)
* Preference redesign + search
* Notification improvements
* Different screens for feed info and settings
* Sort Queue with Random or Smart Shuffle
* True Black Theme for AMOLED
* Improvements to feed parsing
* Fix for app being killed by Android Oreo
Version 1.6.5
-------------
* Fix database corruption
* Improvements to Feed parsing
Version 1.6.4
-------------
* Fixes issues on Android Oreo
* Avoids duplicate chapters
* Experimental: Database import & export
Version 1.6.3 Version 1.6.3
------------- -------------

View File

@ -5,7 +5,7 @@ This is the official repository of AntennaPod, the easy-to-use, flexible and ope
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play" alt="Get it on Google Play"
height="90">](https://play.google.com/store/apps/details?id=de.danoeh.antennapod) height="90">](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)
[<img src="https://f-droid.org/badge/get-it-on.png" [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid" alt="Get it on F-Droid"
height="90">](https://f-droid.org/app/de.danoeh.antennapod) height="90">](https://f-droid.org/app/de.danoeh.antennapod)

View File

@ -1,7 +1,6 @@
import org.apache.tools.ant.filters.ReplaceTokens import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: "com.android.application" apply plugin: "com.android.application"
apply plugin: "me.tatarka.retrolambda"
apply plugin: 'com.github.triplet.play' apply plugin: 'com.github.triplet.play'
apply plugin: 'com.getkeepsafe.dexcount' apply plugin: 'com.getkeepsafe.dexcount'
@ -15,7 +14,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.1' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
} }
} }
@ -84,9 +83,17 @@ android {
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
resValue "string", "provider_authority", "de.danoeh.antennapod.debug.provider" resValue "string", "provider_authority", "de.danoeh.antennapod.debug.provider"
buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey
buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
dexcount {
if (project.hasProperty("enableDexcountInDebug")) {
runOnEachPackage enableDexcountInDebug.toBoolean()
} else { // default to not running dexcount
runOnEachPackage false
}
}
} }
release { release {
resValue "string", "provider_authority", "de.danoeh.antennapod.provider"
minifyEnabled true minifyEnabled true
proguardFile "proguard.cfg" proguardFile "proguard.cfg"
signingConfig signingConfigs.releaseConfig signingConfig signingConfigs.releaseConfig
@ -113,10 +120,13 @@ android {
additionalParameters "--no-version-vectors" additionalParameters "--no-version-vectors"
} }
flavorDimensions "market"
productFlavors { productFlavors {
free { free {
dimension "market"
} }
play { play {
dimension "market"
} }
} }
@ -125,59 +135,55 @@ android {
} }
} }
configurations {
freeDebugCompile
freeReleaseCompile
playDebugCompile
playReleaseCompile
}
dependencies { dependencies {
freeDebugCompile project(path: ":core", configuration: "freeDebug") freeImplementation project(":core")
freeReleaseCompile project(path: ":core", configuration: "freeRelease")
// free build hack: skip some dependencies // free build hack: skip some dependencies
if (!doFreeBuild()) { if (!doFreeBuild()) {
playDebugCompile project(path: ":core", configuration: "playDebug") playImplementation project(":core")
playReleaseCompile project(path: ":core", configuration: "playRelease")
} else { } else {
System.out.println("app: free build hack, skipping some dependencies") System.out.println("app: free build hack, skipping some dependencies")
} }
compile "com.android.support:support-v4:$supportVersion" implementation "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion" implementation "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion" implementation "com.android.support:design:$supportVersion"
compile "com.android.support:gridlayout-v7:$supportVersion" implementation "com.android.support:preference-v14:$supportVersion"
compile "com.android.support:percent:$supportVersion" implementation "com.android.support:gridlayout-v7:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion" implementation "com.android.support:percent:$supportVersion"
compile "org.apache.commons:commons-lang3:$commonslangVersion" implementation "com.android.support:recyclerview-v7:$supportVersion"
compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") { compileOnly 'com.google.android.wearable:wearable:2.2.0'
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
implementation("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json" exclude group: "org.json", module: "json"
} }
compile "commons-io:commons-io:$commonsioVersion" implementation "commons-io:commons-io:$commonsioVersion"
compile "org.jsoup:jsoup:$jsoupVersion" implementation "org.jsoup:jsoup:$jsoupVersion"
compile "com.github.bumptech.glide:glide:$glideVersion" implementation "com.github.bumptech.glide:glide:$glideVersion"
compile "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
compile "com.squareup.okio:okio:$okioVersion" implementation "com.squareup.okio:okio:$okioVersion"
compile "de.greenrobot:eventbus:$eventbusVersion" implementation "de.greenrobot:eventbus:$eventbusVersion"
compile "io.reactivex:rxandroid:$rxAndroidVersion" implementation "io.reactivex:rxandroid:$rxAndroidVersion"
compile "io.reactivex:rxjava:$rxJavaVersion" implementation "io.reactivex:rxjava:$rxJavaVersion"
// And ProGuard rules for RxJava! // And ProGuard rules for RxJava!
compile "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion" implementation "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
compile "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion" implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
compile "com.joanzapata.iconify:android-iconify-material:$iconifyVersion" implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
compile("com.afollestad.material-dialogs:commons:$materialDialogsVersion") { implementation("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
transitive = true transitive = true
} }
compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion" implementation "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
compile("com.githang:viewpagerindicator:2.5@aar") { implementation("com.githang:viewpagerindicator:2.5.1@aar") {
exclude module: "support-v4" exclude module: "support-v4"
} }
compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion" implementation "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion" implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
compile 'com.github.mfietz:fyydlin:v0.3' implementation 'com.github.mfietz:fyydlin:v0.3'
implementation 'com.github.ByteHamster:SearchPreference:v1.0.8'
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
} }
play { play {

View File

@ -125,3 +125,13 @@
-keep class com.squareup.moshi.** { *; } -keep class com.squareup.moshi.** { *; }
-keep interface com.squareup.moshi.** { *; } -keep interface com.squareup.moshi.** { *; }
-keep public class retrofit2.adapter.rxjava.RxJavaCallAdapterFactory { *; } -keep public class retrofit2.adapter.rxjava.RxJavaCallAdapterFactory { *; }
# awaitility
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.Introspector
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.PropertyDescriptor
-dontwarn java.lang.management.ManagementFactory
-dontwarn java.lang.management.ThreadInfo
-dontwarn java.lang.management.ThreadMXBean

View File

@ -0,0 +1,34 @@
{
"data": [
{
"title": "FLOSS Weekly 482: PyPI",
"status_label": "NEW",
"duration": "00:52:40",
"published_at": "2. May"
},
{
"title": "FLOSS Weekly 479: Pidgin",
"status_label": " ",
"duration": "01:08:08",
"published_at": "11. Apr"
},
{
"title": "Linux Outlaws 370 - Stay Free, Stay Open Source",
"status_label": "NEW",
"duration": "02:52:51",
"published_at": "29. Dec 2014"
},
{
"title": "Linux Outlaws 368 - The Dark Ages of Free Software",
"status_label": " ",
"duration": "02:26:54",
"published_at": "14. Dec 2014"
},
{
"title": "Linux Outlaws 365 - Last Stand",
"status_label": " ",
"duration": "00:39:59",
"published_at": "3. Nov 2014"
}
]
}

View File

@ -0,0 +1,2 @@
@null
@drawable/ic_list_grey600_24dp

View File

@ -0,0 +1,3 @@
@drawable/ic_play_arrow_grey600_36dp
@drawable/ic_file_download_grey600_24dp
@drawable/ic_cancel_grey600_24dp

View File

@ -0,0 +1,37 @@
package de.test.antennapod.feed;
import android.test.AndroidTestCase;
import de.danoeh.antennapod.core.feed.FeedItem;
public class FeedItemTest extends AndroidTestCase {
private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
private static final String TEXT_SHORT = "Lorem ipsum";
/**
* If one of `description` or `content:encoded` is null, use the other one.
*/
public void testShownotesNullValues() throws Exception {
testShownotes(null, TEXT_LONG);
testShownotes(TEXT_LONG, null);
}
/**
* If `description` is reasonably longer than `content:encoded`, use `description`.
*/
public void testShownotesLength() throws Exception {
testShownotes(TEXT_SHORT, TEXT_LONG);
testShownotes(TEXT_LONG, TEXT_SHORT);
}
/**
* Checks if the shownotes equal TEXT_LONG, using the given `description` and `content:encoded`
* @param description Description of the feed item
* @param contentEncoded `content:encoded` of the feed item
*/
private void testShownotes(String description, String contentEncoded) throws Exception {
FeedItem item = new FeedItem();
item.setDescription(description);
item.setContentEncoded(contentEncoded);
assertEquals(TEXT_LONG, item.loadShownotes().call());
}
}

View File

@ -2,14 +2,16 @@ package de.test.antennapod.gpodnet;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.gpoddernet.GpodnetService; import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException; import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag;
import java.util.ArrayList; import static java.util.Collections.singletonList;
import java.util.Arrays;
import java.util.List;
/** /**
* Test class for GpodnetService * Test class for GpodnetService
@ -55,7 +57,7 @@ public class GPodnetServiceTest extends AndroidTestCase {
authenticate(); authenticate();
String[] URLS = {"http://bitsundso.de/feed", "http://gamesundso.de/feed", "http://cre.fm/feed/mp3/", "http://freakshow.fm/feed/m4a/"}; String[] URLS = {"http://bitsundso.de/feed", "http://gamesundso.de/feed", "http://cre.fm/feed/mp3/", "http://freakshow.fm/feed/m4a/"};
List<String> subscriptions = Arrays.asList(URLS[0], URLS[1]); List<String> subscriptions = Arrays.asList(URLS[0], URLS[1]);
List<String> removed = Arrays.asList(URLS[0]); List<String> removed = singletonList(URLS[0]);
List<String> added = Arrays.asList(URLS[2], URLS[3]); List<String> added = Arrays.asList(URLS[2], URLS[3]);
service.uploadSubscriptions(USER, "radio", subscriptions); service.uploadSubscriptions(USER, "radio", subscriptions);
service.uploadChanges(USER, "radio", added, removed); service.uploadChanges(USER, "radio", added, removed);

View File

@ -17,7 +17,6 @@ import javax.xml.parsers.ParserConfigurationException;
import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler; import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
@ -32,8 +31,8 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
public class FeedHandlerTest extends InstrumentationTestCase { public class FeedHandlerTest extends InstrumentationTestCase {
private static final String FEEDS_DIR = "testfeeds"; private static final String FEEDS_DIR = "testfeeds";
File file = null; private File file = null;
OutputStream outputStream = null; private OutputStream outputStream = null;
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
@ -82,15 +81,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(feed.getLink(), parsedFeed.getLink()); assertEquals(feed.getLink(), parsedFeed.getLink());
assertEquals(feed.getDescription(), parsedFeed.getDescription()); assertEquals(feed.getDescription(), parsedFeed.getDescription());
assertEquals(feed.getPaymentLink(), parsedFeed.getPaymentLink()); assertEquals(feed.getPaymentLink(), parsedFeed.getPaymentLink());
assertEquals(feed.getImageUrl(), parsedFeed.getImageUrl());
if (feed.getImage() != null) {
FeedImage image = feed.getImage();
FeedImage parsedImage = parsedFeed.getImage();
assertNotNull(parsedImage);
assertEquals(image.getTitle(), parsedImage.getTitle());
assertEquals(image.getDownload_url(), parsedImage.getDownload_url());
}
if (feed.getItems() != null) { if (feed.getItems() != null) {
assertNotNull(parsedFeed.getItems()); assertNotNull(parsedFeed.getItems());
@ -119,14 +110,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(media.getMime_type(), parsedMedia.getMime_type()); assertEquals(media.getMime_type(), parsedMedia.getMime_type());
} }
if (item.hasItemImage()) { assertEquals(item.getImageUrl(), parsedFeed.getImageUrl());
assertTrue(parsedItem.hasItemImage());
FeedImage image = item.getImage();
FeedImage parsedImage = parsedItem.getImage();
assertEquals(image.getTitle(), parsedImage.getTitle());
assertEquals(image.getDownload_url(), parsedImage.getDownload_url());
}
if (item.getChapters() != null) { if (item.getChapters() != null) {
assertNotNull(parsedItem.getChapters()); assertNotNull(parsedItem.getChapters());
@ -158,14 +142,10 @@ public class FeedHandlerTest extends InstrumentationTestCase {
} }
private Feed createTestFeed(int numItems, boolean withImage, boolean withFeedMedia, boolean withChapters) { private Feed createTestFeed(int numItems, boolean withImage, boolean withFeedMedia, boolean withChapters) {
FeedImage image = null;
if (withImage) {
image = new FeedImage(0, "image", null, "http://example.com/picture", false);
}
Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description", Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description",
"http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", image, file.getAbsolutePath(), "http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", "http://example.com/picture", file.getAbsolutePath(),
"http://example.com/feed", true); "http://example.com/feed", true);
feed.setItems(new ArrayList<FeedItem>()); feed.setItems(new ArrayList<>());
for (int i = 0; i < numItems; i++) { for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i, FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i,

View File

@ -23,8 +23,8 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType; 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.LocalPSMP;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus; import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;

View File

@ -9,10 +9,10 @@ import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager; import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;

View File

@ -27,12 +27,12 @@ import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
public class DBCleanupTests extends InstrumentationTestCase { public class DBCleanupTests extends InstrumentationTestCase {
private static final String TAG = "DBTasksTest"; private static final String TAG = "DBTasksTest";
protected static final int EPISODE_CACHE_SIZE = 5; static final int EPISODE_CACHE_SIZE = 5;
private final int cleanupAlgorithm; private final int cleanupAlgorithm;
protected Context context; Context context;
protected File destFolder; private File destFolder;
public DBCleanupTests() { public DBCleanupTests() {
this.cleanupAlgorithm = UserPreferences.EPISODE_CLEANUP_DEFAULT; this.cleanupAlgorithm = UserPreferences.EPISODE_CLEANUP_DEFAULT;
@ -104,9 +104,9 @@ public class DBCleanupTests extends InstrumentationTestCase {
} }
} }
protected void populateItems(final int numItems, Feed feed, List<FeedItem> items, void populateItems(final int numItems, Feed feed, List<FeedItem> items,
List<File> files, int itemState, boolean addToQueue, List<File> files, int itemState, boolean addToQueue,
boolean addToFavorites) throws IOException { boolean addToFavorites) throws IOException {
for (int i = 0; i < numItems; i++) { for (int i = 0; i < numItems; i++) {
Date itemDate = new Date(numItems - i); Date itemDate = new Date(numItems - i);
Date playbackCompletionDate = null; Date playbackCompletionDate = null;

View File

@ -5,7 +5,6 @@ import android.test.FlakyTest;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;

View File

@ -18,6 +18,8 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import static java.util.Collections.singletonList;
/** /**
* Test class for DBTasks * Test class for DBTasks
*/ */
@ -125,7 +127,7 @@ public class DBTasksTest extends InstrumentationTestCase {
public void testUpdateFeedMediaUrlResetState() { public void testUpdateFeedMediaUrlResetState() {
final Feed feed = new Feed("url", null, "title"); final Feed feed = new Feed("url", null, "title");
FeedItem item = new FeedItem(0, "item", "id", "link", new Date(), FeedItem.PLAYED, feed); FeedItem item = new FeedItem(0, "item", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.setItems(Arrays.asList(item)); feed.setItems(singletonList(item));
PodDBAdapter adapter = PodDBAdapter.getInstance(); PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
@ -138,7 +140,7 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedMedia media = new FeedMedia(item, "url", 1024, "mime/type"); FeedMedia media = new FeedMedia(item, "url", 1024, "mime/type");
item.setMedia(media); item.setMedia(media);
feed.setItems(Arrays.asList(item)); feed.setItems(singletonList(item));
final Feed newFeed = DBTasks.updateFeed(context, feed)[0]; final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(feed != newFeed); assertTrue(feed != newFeed);

View File

@ -19,7 +19,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
/** /**
* Utility methods for DB* tests. * Utility methods for DB* tests.
*/ */
public class DBTestUtils { class DBTestUtils {
private DBTestUtils(){} private DBTestUtils(){}
/** /**

View File

@ -15,9 +15,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.SimpleChapter; import de.danoeh.antennapod.core.feed.SimpleChapter;
@ -124,89 +122,13 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNull(media.getFile_url()); assertNull(media.getFile_url());
} }
public void testDeleteFeed() throws IOException, ExecutionException, InterruptedException, TimeoutException { public void testDeleteFeed() throws ExecutionException, InterruptedException, IOException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER); File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder); assertNotNull(destFolder);
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>()); feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed, true);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
assertTrue(enc.createNewFile());
itemFiles.add(enc);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0);
item.setMedia(media);
item.setChapters(new ArrayList<Chapter>());
item.getChapters().add(new SimpleChapter(0, "item " + i, item, "example.com"));
}
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
assertTrue(item.getChapters().get(0).getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
for (File f : itemFiles) {
assertFalse(f.exists());
}
adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertEquals(0, c.getCount());
c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertEquals(0, c.getCount());
c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertEquals(0, c.getCount());
c.close();
c = adapter.getSingleFeedMediaCursor(item.getMedia().getId());
assertEquals(0, c.getCount());
c.close();
c = adapter.getSimpleChaptersOfFeedItemCursor(item);
assertEquals(0, c.getCount());
c.close();
}
adapter.close();
}
public void testDeleteFeedNoImage() throws ExecutionException, InterruptedException, IOException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
feed.setImage(null);
List<File> itemFiles = new ArrayList<>(); List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files // create items with downloaded media files
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -261,13 +183,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(null); feed.setItems(null);
feed.setImageUrl("url");
// create Feed image
File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
PodDBAdapter adapter = PodDBAdapter.getInstance(); PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
@ -275,21 +191,14 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close(); adapter.close();
assertTrue(feed.getId() != 0); assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS); DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
adapter = PodDBAdapter.getInstance(); adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
adapter.close(); adapter.close();
} }
@ -300,12 +209,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>()); feed.setItems(new ArrayList<>());
// create Feed image feed.setImageUrl("url");
File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
// create items // create items
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -320,24 +224,18 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close(); adapter.close();
assertTrue(feed.getId() != 0); assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0); assertTrue(item.getId() != 0);
} }
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS); DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
adapter = PodDBAdapter.getInstance(); adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId())); c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
@ -346,65 +244,6 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close(); adapter.close();
} }
public void testDeleteFeedWithItemImages() throws InterruptedException, ExecutionException, TimeoutException, IOException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
assertTrue(imgFile.createNewFile());
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
// create items with images
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File itemImageFile = new File(destFolder, "item-image-" + i);
FeedImage itemImage = new FeedImage(0, "item-image" + i, itemImageFile.getAbsolutePath(), "url", true);
item.setImage(itemImage);
}
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
assertTrue(item.getImage().getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
// check if files still exist
assertFalse(imgFile.exists());
adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
c.close();
c = adapter.getImageCursor(String.valueOf(item.getImage().getId()));
assertEquals(0, c.getCount());
c.close();
}
adapter.close();
}
public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException { public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER); File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder); assertNotNull(destFolder);
@ -412,11 +251,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>()); feed.setItems(new ArrayList<>());
// create Feed image feed.setImageUrl("url");
File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
List<File> itemFiles = new ArrayList<>(); List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files // create items with downloaded media files
@ -437,7 +272,6 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close(); adapter.close();
assertTrue(feed.getId() != 0); assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0); assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0); assertTrue(item.getMedia().getId() != 0);
@ -460,9 +294,6 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId())); c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
@ -484,11 +315,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>()); feed.setItems(new ArrayList<>());
// create Feed image feed.setImageUrl("url");
File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
List<File> itemFiles = new ArrayList<>(); List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files // create items with downloaded media files
@ -509,7 +336,6 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close(); adapter.close();
assertTrue(feed.getId() != 0); assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0); assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0); assertTrue(item.getMedia().getId() != 0);
@ -522,9 +348,6 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId()); Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
c.close(); c.close();
c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId())); c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0); assertTrue(c.getCount() == 0);
@ -539,7 +362,7 @@ public class DBWriterTest extends InstrumentationTestCase {
private FeedMedia playbackHistorySetup(Date playbackCompletionDate) { private FeedMedia playbackHistorySetup(Date playbackCompletionDate) {
final Context context = getInstrumentation().getTargetContext(); final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<FeedItem>()); feed.setItems(new ArrayList<>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed); FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0); FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0);
feed.getItems().add(item); feed.getItems().add(item);
@ -791,7 +614,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException { public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10; final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title"); Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<FeedItem>()); feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) { for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed); FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item); feed.getItems().add(item);

View File

@ -15,6 +15,7 @@ import java.util.List;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
@ -22,7 +23,6 @@ import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.preferences.PreferenceController;
/** /**
* User interface tests for MainActivity * User interface tests for MainActivity
@ -96,7 +96,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testClickNavDrawer() throws Exception { public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false); uiTestUtils.addLocalFeedData(false);
UserPreferences.setHiddenDrawerItems(new ArrayList<String>()); UserPreferences.setHiddenDrawerItems(new ArrayList<>());
// queue // queue
openNavDrawer(); openNavDrawer();
@ -155,11 +155,11 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testGoToPreferences() { public void testGoToPreferences() {
openNavDrawer(); openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label)); solo.clickOnText(solo.getString(R.string.settings_label));
solo.waitForActivity(PreferenceController.getPreferenceActivity()); solo.waitForActivity(PreferenceActivity.class);
} }
public void testDrawerPreferencesHideSomeElements() { public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>()); UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer(); openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label)); solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
@ -190,7 +190,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
} }
public void testDrawerPreferencesHideAllElements() { public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>()); UserPreferences.setHiddenDrawerItems(new ArrayList<>());
String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles); String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer(); openNavDrawer();
@ -209,7 +209,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
} }
public void testDrawerPreferencesHideCurrentElement() { public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>()); UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer(); openNavDrawer();
String downloads = solo.getString(R.string.downloads_label); String downloads = solo.getString(R.string.downloads_label);

View File

@ -33,8 +33,8 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> { public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> {
private static final String TAG = PlaybackTest.class.getSimpleName(); private static final String TAG = PlaybackTest.class.getSimpleName();
public static final int EPISODES_DRAWER_LIST_INDEX = 1; private static final int EPISODES_DRAWER_LIST_INDEX = 1;
public static final int QUEUE_DRAWER_LIST_INDEX = 0; private static final int QUEUE_DRAWER_LIST_INDEX = 0;
private Solo solo; private Solo solo;
private UITestUtils uiTestUtils; private UITestUtils uiTestUtils;

View File

@ -30,8 +30,8 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity> { public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity> {
private static final String TAG = PlaybackTest.class.getSimpleName(); private static final String TAG = PlaybackTest.class.getSimpleName();
public static final int EPISODES_DRAWER_LIST_INDEX = 1; private static final int EPISODES_DRAWER_LIST_INDEX = 1;
public static final int QUEUE_DRAWER_LIST_INDEX = 0; private static final int QUEUE_DRAWER_LIST_INDEX = 0;
private Solo solo; private Solo solo;
private UITestUtils uiTestUtils; private UITestUtils uiTestUtils;

View File

@ -3,7 +3,6 @@ package de.test.antennapod.ui;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import com.robotium.solo.Solo; import com.robotium.solo.Solo;
import com.robotium.solo.Timeout; import com.robotium.solo.Timeout;
@ -56,6 +55,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} else { } else {
otherTheme = R.string.pref_theme_title_light; otherTheme = R.string.pref_theme_title_light;
} }
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_set_theme_title)); solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(otherTheme)); solo.clickOnText(solo.getString(otherTheme));
@ -70,6 +70,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} else { } else {
otherTheme = R.string.pref_theme_title_light; otherTheme = R.string.pref_theme_title_light;
} }
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_set_theme_title)); solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
solo.waitForDialogToOpen(1000); solo.waitForDialogToOpen(1000);
solo.clickOnText(solo.getString(otherTheme)); solo.clickOnText(solo.getString(otherTheme));
@ -77,6 +78,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testExpandNotification() { public void testExpandNotification() {
solo.clickOnText(solo.getString(R.string.user_interface_label));
final int priority = UserPreferences.getNotifyPriority(); final int priority = UserPreferences.getNotifyPriority();
solo.clickOnText(solo.getString(R.string.pref_expandNotify_title)); solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
assertTrue(solo.waitForCondition(() -> priority != UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> priority != UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
@ -85,7 +87,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testEnablePersistentPlaybackControls() { public void testEnablePersistentPlaybackControls() {
solo.clickOnText(solo.getString(R.string.user_interface_label));
final boolean persistNotify = UserPreferences.isPersistNotify(); final boolean persistNotify = UserPreferences.isPersistNotify();
solo.scrollDown();
solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_persistNotify_title)); solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_persistNotify_title)); solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
@ -93,14 +98,16 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testSetLockscreenButtons() { public void testSetLockscreenButtons() {
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.scrollDown();
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options); String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
solo.clickOnText(solo.getString(R.string.pref_compact_notification_buttons_title)); solo.clickOnText(solo.getString(R.string.pref_compact_notification_buttons_title));
solo.waitForDialogToOpen(1000); solo.waitForDialogToOpen(1000);
// First uncheck every checkbox // First uncheck every checkbox
for (int i=0; i<buttons.length; i++) { for (String button : buttons) {
assertTrue(solo.searchText(buttons[i])); assertTrue(solo.searchText(button));
if (solo.isTextChecked(buttons[i])) { if (solo.isTextChecked(button)) {
solo.clickOnText(buttons[i]); solo.clickOnText(button);
} }
} }
// Now try to check all checkboxes // Now try to check all checkboxes
@ -111,13 +118,16 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(!solo.isTextChecked(buttons[2])); assertTrue(!solo.isTextChecked(buttons[2]));
solo.clickOnText(solo.getString(R.string.confirm_label)); solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose(1000); solo.waitForDialogToClose(1000);
assertTrue(solo.waitForCondition(() -> UserPreferences.showRewindOnCompactNotification(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.showFastForwardOnCompactNotification(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
} }
public void testEnqueueAtFront() { public void testEnqueueAtFront() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean enqueueAtFront = UserPreferences.enqueueAtFront(); final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
solo.scrollDown();
solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title)); solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title)); solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
@ -125,6 +135,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testHeadPhonesDisconnect() { public void testHeadPhonesDisconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect(); final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title)); solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
@ -133,9 +144,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testHeadPhonesReconnect() { public void testHeadPhonesReconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) { if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title)); solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
} }
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect(); final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title)); solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
@ -145,9 +157,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testBluetoothReconnect() { public void testBluetoothReconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) { if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title)); solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
} }
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect(); final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title)); solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
@ -157,7 +170,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testContinuousPlayback() { public void testContinuousPlayback() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean continuousPlayback = UserPreferences.isFollowQueue(); final boolean continuousPlayback = UserPreferences.isFollowQueue();
solo.scrollDown();
solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_followQueue_title)); solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_followQueue_title)); solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
@ -165,6 +181,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testAutoDelete() { public void testAutoDelete() {
solo.clickOnText(solo.getString(R.string.storage_pref));
final boolean autoDelete = UserPreferences.isAutoDelete(); final boolean autoDelete = UserPreferences.isAutoDelete();
solo.clickOnText(solo.getString(R.string.pref_auto_delete_title)); solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
@ -173,6 +190,9 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testPlaybackSpeeds() { public void testPlaybackSpeeds() {
solo.clickOnText(solo.getString(R.string.playback_pref));
solo.scrollDown();
solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_playback_speed_title)); solo.clickOnText(solo.getString(R.string.pref_playback_speed_title));
solo.waitForDialogToOpen(1000); solo.waitForDialogToOpen(1000);
assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0])); assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0]));
@ -181,6 +201,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testPauseForInterruptions() { public void testPauseForInterruptions() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss(); final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title)); solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
@ -189,6 +210,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testDisableUpdateInterval() { public void testDisableUpdateInterval() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum)); solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable)); solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
@ -196,6 +218,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testSetUpdateInterval() { public void testSetUpdateInterval() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_title)); solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_title));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Interval)); solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
@ -208,6 +231,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testMobileUpdates() { public void testMobileUpdates() {
solo.clickOnText(solo.getString(R.string.network_pref));
final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate(); final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate();
solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title)); solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout())); assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
@ -216,6 +240,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testSetSequentialDownload() { public void testSetSequentialDownload() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title)); solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clearEditText(0); solo.clearEditText(0);
@ -225,6 +250,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testSetParallelDownloads() { public void testSetParallelDownloads() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title)); solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clearEditText(0); solo.clearEditText(0);
@ -234,6 +260,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testSetParallelDownloadsInvalidInput() { public void testSetParallelDownloadsInvalidInput() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title)); solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
solo.clearEditText(0); solo.clearEditText(0);
@ -249,6 +276,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
String[] values = res.getStringArray(R.array.episode_cache_size_values); String[] values = res.getStringArray(R.array.episode_cache_size_values);
String entry = entries[entries.length/2]; String entry = entries[entries.length/2];
final int value = Integer.valueOf(values[values.length/2]); final int value = Integer.valueOf(values[values.length/2]);
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title)); solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.waitForText(solo.getString(R.string.pref_automatic_download_title)); solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title)); solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
@ -262,6 +290,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
String[] values = res.getStringArray(R.array.episode_cache_size_values); String[] values = res.getStringArray(R.array.episode_cache_size_values);
String minEntry = entries[0]; String minEntry = entries[0];
final int minValue = Integer.valueOf(values[0]); final int minValue = Integer.valueOf(values[0]);
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title)); solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.waitForText(solo.getString(R.string.pref_automatic_download_title)); solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
if(!UserPreferences.isEnableAutodownload()) { if(!UserPreferences.isEnableAutodownload()) {
@ -279,6 +308,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
String[] values = res.getStringArray(R.array.episode_cache_size_values); String[] values = res.getStringArray(R.array.episode_cache_size_values);
String maxEntry = entries[entries.length-1]; String maxEntry = entries[entries.length-1];
final int maxValue = Integer.valueOf(values[values.length-1]); final int maxValue = Integer.valueOf(values[values.length-1]);
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title)); solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.waitForText(solo.getString(R.string.pref_automatic_download_title)); solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
if(!UserPreferences.isEnableAutodownload()) { if(!UserPreferences.isEnableAutodownload()) {
@ -292,6 +322,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
public void testAutomaticDownload() { public void testAutomaticDownload() {
final boolean automaticDownload = UserPreferences.isEnableAutodownload(); final boolean automaticDownload = UserPreferences.isEnableAutodownload();
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title)); solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.waitForText(solo.getString(R.string.pref_automatic_download_title)); solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title)); solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
@ -313,6 +344,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testEpisodeCleanupQueueOnly() { public void testEpisodeCleanupQueueOnly() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title)); solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal)); solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
solo.clickOnText(solo.getString(R.string.episode_cleanup_queue_removal)); solo.clickOnText(solo.getString(R.string.episode_cleanup_queue_removal));
@ -324,6 +357,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testEpisodeCleanupNeverAlg() { public void testEpisodeCleanupNeverAlg() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title)); solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_never)); solo.waitForText(solo.getString(R.string.episode_cleanup_never));
solo.clickOnText(solo.getString(R.string.episode_cleanup_never)); solo.clickOnText(solo.getString(R.string.episode_cleanup_never));
@ -335,6 +370,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testEpisodeCleanupClassic() { public void testEpisodeCleanupClassic() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title)); solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening)); solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
solo.clickOnText(solo.getString(R.string.episode_cleanup_after_listening)); solo.clickOnText(solo.getString(R.string.episode_cleanup_after_listening));
@ -350,6 +387,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testEpisodeCleanupNumDays() { public void testEpisodeCleanupNumDays() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title)); solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening)); solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
solo.clickOnText("5"); solo.clickOnText("5");
@ -369,6 +408,9 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
int seconds = UserPreferences.getRewindSecs(); int seconds = UserPreferences.getRewindSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values); int deltas[] = res.getIntArray(R.array.seek_delta_values);
solo.clickOnText(solo.getString(R.string.playback_pref));
solo.scrollDown();
solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_rewind)); solo.clickOnText(solo.getString(R.string.pref_rewind));
solo.waitForDialogToOpen(); solo.waitForDialogToOpen();
@ -387,6 +429,9 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} }
public void testFastForwardChange() { public void testFastForwardChange() {
solo.clickOnText(solo.getString(R.string.playback_pref));
solo.scrollDown();
solo.scrollDown();
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs(); int seconds = UserPreferences.getFastForwardSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values); int deltas[] = res.getIntArray(R.array.seek_delta_values);

View File

@ -1,9 +1,7 @@
package de.test.antennapod.ui; package de.test.antennapod.ui;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log; import android.util.Log;
import junit.framework.Assert; import junit.framework.Assert;
@ -20,14 +18,12 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
@ -39,27 +35,25 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
* Utility methods for UI tests. * Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images. * Starts a web server that hosts feeds, episodes and images.
*/ */
@TargetApi(Build.VERSION_CODES.HONEYCOMB) class UITestUtils {
public class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName(); private static final String TAG = UITestUtils.class.getSimpleName();
private static final String DATA_FOLDER = "test/UITestUtils"; private static final String DATA_FOLDER = "test/UITestUtils";
public static final int NUM_FEEDS = 5; private static final int NUM_FEEDS = 5;
public static final int NUM_ITEMS_PER_FEED = 10; private static final int NUM_ITEMS_PER_FEED = 10;
public static final int HOME_VIEW = (Build.VERSION.SDK_INT >= 11) ? android.R.id.home : R.id.home; private static final String TEST_FILE_NAME = "3sec.mp3";
public static final String TEST_FILE_NAME = "3sec.mp3";
private Context context; private final Context context;
private HTTPBin server = new HTTPBin(); private final HTTPBin server = new HTTPBin();
private File destDir; private File destDir;
private File hostedFeedDir; private File hostedFeedDir;
private File hostedMediaDir; private File hostedMediaDir;
public List<Feed> hostedFeeds = new ArrayList<>(); public final List<Feed> hostedFeeds = new ArrayList<>();
public UITestUtils(Context context) { public UITestUtils(Context context) {
this.context = context; this.context = context;
@ -141,12 +135,9 @@ public class UITestUtils {
public void addHostedFeedData() throws IOException { public void addHostedFeedData() throws IOException {
if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance"); if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance");
for (int i = 0; i < NUM_FEEDS; i++) { for (int i = 0; i < NUM_FEEDS; i++) {
File bitmapFile = newBitmapFile("image" + i);
FeedImage image = new FeedImage(0, "image " + i, null, hostFile(bitmapFile), false);
Feed feed = new Feed(0, null, "Title " + i, "http://example.com/" + i, "Description of feed " + i, Feed feed = new Feed(0, null, "Title " + i, "http://example.com/" + i, "Description of feed " + i,
"http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, image, null, "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, null, null,
"http://example.com/feed/src/" + i, false); "http://example.com/feed/src/" + i, false);
image.setOwner(feed);
// create items // create items
List<FeedItem> items = new ArrayList<>(); List<FeedItem> items = new ArrayList<>();
@ -192,12 +183,6 @@ public class UITestUtils {
List<FeedItem> queue = new ArrayList<>(); List<FeedItem> queue = new ArrayList<>();
for (Feed feed : hostedFeeds) { for (Feed feed : hostedFeeds) {
feed.setDownloaded(true); feed.setDownloaded(true);
if (feed.getImage() != null) {
FeedImage image = feed.getImage();
int fileId = Integer.parseInt(StringUtils.substringAfter(image.getDownload_url(), "files/"));
image.setFile_url(server.accessFile(fileId).getAbsolutePath());
image.setDownloaded(true);
}
if (downloadEpisodes) { if (downloadEpisodes) {
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
if (item.hasMedia()) { if (item.hasMedia()) {

View File

@ -38,9 +38,6 @@ public class UITestUtilsTest extends InstrumentationTestCase {
for (Feed feed : feeds) { for (Feed feed : feeds) {
testUrlReachable(feed.getDownload_url()); testUrlReachable(feed.getDownload_url());
if (feed.getImage() != null) {
testUrlReachable(feed.getImage().getDownload_url());
}
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
if (item.hasMedia()) { if (item.hasMedia()) {
testUrlReachable(item.getMedia().getDownload_url()); testUrlReachable(item.getMedia().getDownload_url());
@ -66,9 +63,6 @@ public class UITestUtilsTest extends InstrumentationTestCase {
for (Feed feed : uiTestUtils.hostedFeeds) { for (Feed feed : uiTestUtils.hostedFeeds) {
assertTrue(feed.getId() != 0); assertTrue(feed.getId() != 0);
if (feed.getImage() != null) {
assertTrue(feed.getImage().getId() != 0);
}
for (FeedItem item : feed.getItems()) { for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0); assertTrue(item.getId() != 0);
if (item.hasMedia()) { if (item.hasMedia()) {

View File

@ -1,11 +1,12 @@
package de.test.antennapod.util; package de.test.antennapod.util;
import android.test.AndroidTestCase;
import android.text.TextUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import de.danoeh.antennapod.core.util.FileNameGenerator; import de.danoeh.antennapod.core.util.FileNameGenerator;
import android.test.AndroidTestCase;
import android.text.TextUtils;
public class FilenameGeneratorTest extends AndroidTestCase { public class FilenameGeneratorTest extends AndroidTestCase {

View File

@ -1,8 +1,8 @@
package de.test.antennapod.util; package de.test.antennapod.util;
import junit.framework.*; import junit.framework.TestCase;
import de.danoeh.antennapod.core.util.*; import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
/** /**
* Tests for {@link RewindAfterPauseUtils}. * Tests for {@link RewindAfterPauseUtils}.

View File

@ -1,6 +1,7 @@
package de.test.antennapod.util; package de.test.antennapod.util;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import de.danoeh.antennapod.core.util.URIUtil; import de.danoeh.antennapod.core.util.URIUtil;
/** /**

View File

@ -1,6 +1,7 @@
package de.test.antennapod.util; package de.test.antennapod.util;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.URLChecker;
/** /**

View File

@ -45,7 +45,7 @@ public class HTTPBin extends NanoHTTPD {
private static final String MIME_HTML = "text/html"; private static final String MIME_HTML = "text/html";
private static final String MIME_PLAIN = "text/plain"; private static final String MIME_PLAIN = "text/plain";
private List<File> servedFiles; private final List<File> servedFiles;
public HTTPBin() { public HTTPBin() {
super(PORT); super(PORT);

View File

@ -88,15 +88,15 @@ public abstract class NanoHTTPD {
* This is required as the Keep-Alive HTTP connections would otherwise * This is required as the Keep-Alive HTTP connections would otherwise
* block the socket reading thread forever (or as long the browser is open). * block the socket reading thread forever (or as long the browser is open).
*/ */
public static final int SOCKET_READ_TIMEOUT = 5000; private static final int SOCKET_READ_TIMEOUT = 5000;
/** /**
* Common mime type for dynamic content: plain text * Common mime type for dynamic content: plain text
*/ */
public static final String MIME_PLAINTEXT = "text/plain"; private static final String MIME_PLAINTEXT = "text/plain";
/** /**
* Common mime type for dynamic content: html * Common mime type for dynamic content: html
*/ */
public static final String MIME_HTML = "text/html"; private static final String MIME_HTML = "text/html";
/** /**
* Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing. * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
*/ */
@ -104,7 +104,7 @@ public abstract class NanoHTTPD {
private final String hostname; private final String hostname;
private final int myPort; private final int myPort;
private ServerSocket myServerSocket; private ServerSocket myServerSocket;
private Set<Socket> openConnections = new HashSet<>(); private final Set<Socket> openConnections = new HashSet<>();
private Thread myThread; private Thread myThread;
/** /**
* Pluggable strategy for asynchronously executing requests. * Pluggable strategy for asynchronously executing requests.
@ -118,14 +118,14 @@ public abstract class NanoHTTPD {
/** /**
* Constructs an HTTP server on given port. * Constructs an HTTP server on given port.
*/ */
public NanoHTTPD(int port) { NanoHTTPD(int port) {
this(null, port); this(null, port);
} }
/** /**
* Constructs an HTTP server on given hostname and port. * Constructs an HTTP server on given hostname and port.
*/ */
public NanoHTTPD(String hostname, int port) { private NanoHTTPD(String hostname, int port) {
this.hostname = hostname; this.hostname = hostname;
this.myPort = port; this.myPort = port;
setTempFileManagerFactory(new DefaultTempFileManagerFactory()); setTempFileManagerFactory(new DefaultTempFileManagerFactory());
@ -168,44 +168,38 @@ public abstract class NanoHTTPD {
myServerSocket = new ServerSocket(); myServerSocket = new ServerSocket();
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
myThread = new Thread(new Runnable() { myThread = new Thread(() -> {
@Override do {
public void run() { try {
do { final Socket finalAccept = myServerSocket.accept();
try { registerConnection(finalAccept);
final Socket finalAccept = myServerSocket.accept(); finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
registerConnection(finalAccept); final InputStream inputStream = finalAccept.getInputStream();
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT); asyncRunner.exec(() -> {
final InputStream inputStream = finalAccept.getInputStream(); OutputStream outputStream = null;
asyncRunner.exec(new Runnable() { try {
@Override outputStream = finalAccept.getOutputStream();
public void run() { TempFileManager tempFileManager = tempFileManagerFactory.create();
OutputStream outputStream = null; HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
try { while (!finalAccept.isClosed()) {
outputStream = finalAccept.getOutputStream(); session.execute();
TempFileManager tempFileManager = tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
while (!finalAccept.isClosed()) {
session.execute();
}
} catch (Exception e) {
// When the socket is closed by the client, we throw our own SocketException
// to break the "keep alive" loop above.
if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
e.printStackTrace();
}
} finally {
safeClose(outputStream);
safeClose(inputStream);
safeClose(finalAccept);
unRegisterConnection(finalAccept);
}
} }
}); } catch (Exception e) {
} catch (IOException e) { // When the socket is closed by the client, we throw our own SocketException
} // to break the "keep alive" loop above.
} while (!myServerSocket.isClosed()); if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
} e.printStackTrace();
}
} finally {
safeClose(outputStream);
safeClose(inputStream);
safeClose(finalAccept);
unRegisterConnection(finalAccept);
}
});
} catch (IOException e) {
}
} while (!myServerSocket.isClosed());
}); });
myThread.setDaemon(true); myThread.setDaemon(true);
myThread.setName("NanoHttpd Main Listener"); myThread.setName("NanoHttpd Main Listener");
@ -232,7 +226,7 @@ public abstract class NanoHTTPD {
* *
* @param socket the {@link Socket} for the connection. * @param socket the {@link Socket} for the connection.
*/ */
public synchronized void registerConnection(Socket socket) { private synchronized void registerConnection(Socket socket) {
openConnections.add(socket); openConnections.add(socket);
} }
@ -242,14 +236,14 @@ public abstract class NanoHTTPD {
* @param socket * @param socket
* the {@link Socket} for the connection. * the {@link Socket} for the connection.
*/ */
public synchronized void unRegisterConnection(Socket socket) { private synchronized void unRegisterConnection(Socket socket) {
openConnections.remove(socket); openConnections.remove(socket);
} }
/** /**
* Forcibly closes all connections that are open. * Forcibly closes all connections that are open.
*/ */
public synchronized void closeAllConnections() { private synchronized void closeAllConnections() {
for (Socket socket : openConnections) { for (Socket socket : openConnections) {
safeClose(socket); safeClose(socket);
} }
@ -259,7 +253,7 @@ public abstract class NanoHTTPD {
return myServerSocket == null ? -1 : myServerSocket.getLocalPort(); return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
} }
public final boolean wasStarted() { private boolean wasStarted() {
return myServerSocket != null && myThread != null; return myServerSocket != null && myThread != null;
} }
@ -294,7 +288,7 @@ public abstract class NanoHTTPD {
* @param session The HTTP session * @param session The HTTP session
* @return HTTP response, see class Response for details * @return HTTP response, see class Response for details
*/ */
public Response serve(IHTTPSession session) { Response serve(IHTTPSession session) {
Map<String, String> files = new ArrayMap<>(); Map<String, String> files = new ArrayMap<>();
Method method = session.getMethod(); Method method = session.getMethod();
if (Method.PUT.equals(method) || Method.POST.equals(method)) { if (Method.PUT.equals(method) || Method.POST.equals(method)) {
@ -318,7 +312,7 @@ public abstract class NanoHTTPD {
* @param str the percent encoded <code>String</code> * @param str the percent encoded <code>String</code>
* @return expanded form of the input, for example "foo%20bar" becomes "foo bar" * @return expanded form of the input, for example "foo%20bar" becomes "foo bar"
*/ */
protected String decodePercent(String str) { private String decodePercent(String str) {
String decoded = null; String decoded = null;
try { try {
decoded = URLDecoder.decode(str, "UTF8"); decoded = URLDecoder.decode(str, "UTF8");
@ -347,7 +341,7 @@ public abstract class NanoHTTPD {
* @param queryString a query string pulled from the URL. * @param queryString a query string pulled from the URL.
* @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied). * @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
*/ */
protected Map<String, List<String>> decodeParameters(String queryString) { private Map<String, List<String>> decodeParameters(String queryString) {
Map<String, List<String>> parms = new ArrayMap<>(); Map<String, List<String>> parms = new ArrayMap<>();
if (queryString != null) { if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&"); StringTokenizer st = new StringTokenizer(queryString, "&");
@ -378,7 +372,7 @@ public abstract class NanoHTTPD {
* *
* @param asyncRunner new strategy for handling threads. * @param asyncRunner new strategy for handling threads.
*/ */
public void setAsyncRunner(AsyncRunner asyncRunner) { private void setAsyncRunner(AsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner; this.asyncRunner = asyncRunner;
} }
@ -393,7 +387,7 @@ public abstract class NanoHTTPD {
* *
* @param tempFileManagerFactory new strategy for handling temp files. * @param tempFileManagerFactory new strategy for handling temp files.
*/ */
public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { private void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory; this.tempFileManagerFactory = tempFileManagerFactory;
} }
@ -448,9 +442,9 @@ public abstract class NanoHTTPD {
* themselves up when no longer needed.</p> * themselves up when no longer needed.</p>
*/ */
public interface TempFile { public interface TempFile {
OutputStream open() throws Exception; OutputStream open();
void delete() throws Exception; void delete();
String getName(); String getName();
} }
@ -528,12 +522,12 @@ public abstract class NanoHTTPD {
} }
@Override @Override
public OutputStream open() throws Exception { public OutputStream open() {
return fstream; return fstream;
} }
@Override @Override
public void delete() throws Exception { public void delete() {
safeClose(fstream); safeClose(fstream);
file.delete(); file.delete();
} }
@ -563,7 +557,7 @@ public abstract class NanoHTTPD {
/** /**
* Headers for the HTTP response. Use addHeader() to add lines. * Headers for the HTTP response. Use addHeader() to add lines.
*/ */
private Map<String, String> header = new ArrayMap<>(); private final Map<String, String> header = new ArrayMap<>();
/** /**
* The request method that spawned this response. * The request method that spawned this response.
*/ */
@ -616,7 +610,7 @@ public abstract class NanoHTTPD {
/** /**
* Sends given response to the socket. * Sends given response to the socket.
*/ */
protected void send(OutputStream outputStream) { void send(OutputStream outputStream) {
String mime = mimeType; String mime = mimeType;
SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
@ -661,13 +655,13 @@ public abstract class NanoHTTPD {
} }
} }
protected void sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) { void sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) {
if (!headerAlreadySent(header, "content-length")) { if (!headerAlreadySent(header, "content-length")) {
pw.print("Content-Length: "+ size +"\r\n"); pw.print("Content-Length: "+ size +"\r\n");
} }
} }
protected void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) { void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) {
if (!headerAlreadySent(header, "connection")) { if (!headerAlreadySent(header, "connection")) {
pw.print("Connection: keep-alive\r\n"); pw.print("Connection: keep-alive\r\n");
} }
@ -694,7 +688,7 @@ public abstract class NanoHTTPD {
outputStream.write(buff, 0, read); outputStream.write(buff, 0, read);
outputStream.write(CRLF); outputStream.write(CRLF);
} }
outputStream.write(String.format("0\r\n\r\n").getBytes()); outputStream.write("0\r\n\r\n".getBytes());
} }
private void sendAsFixedLength(OutputStream outputStream, int pending) throws IOException { private void sendAsFixedLength(OutputStream outputStream, int pending) throws IOException {
@ -844,7 +838,7 @@ public abstract class NanoHTTPD {
public static final int BUFSIZE = 8192; public static final int BUFSIZE = 8192;
private final TempFileManager tempFileManager; private final TempFileManager tempFileManager;
private final OutputStream outputStream; private final OutputStream outputStream;
private PushbackInputStream inputStream; private final PushbackInputStream inputStream;
private int splitbyte; private int splitbyte;
private int rlen; private int rlen;
private String uri; private String uri;
@ -1144,17 +1138,19 @@ public abstract class NanoHTTPD {
String value = ""; String value = "";
if (item.get("content-type") == null) { if (item.get("content-type") == null) {
StringBuilder tmp = new StringBuilder();
while (mpline != null && !mpline.contains(boundary)) { while (mpline != null && !mpline.contains(boundary)) {
mpline = in.readLine(); mpline = in.readLine();
if (mpline != null) { if (mpline != null) {
int d = mpline.indexOf(boundary); int d = mpline.indexOf(boundary);
if (d == -1) { if (d == -1) {
value += mpline; tmp.append(mpline);
} else { } else {
value += mpline.substring(0, d - 2); tmp.append(mpline.substring(0, d - 2));
} }
} }
} }
value = tmp.toString();
} else { } else {
if (boundarycount > bpositions.length) { if (boundarycount > bpositions.length) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request"); throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
@ -1326,7 +1322,9 @@ public abstract class NanoHTTPD {
} }
public static class Cookie { public static class Cookie {
private String n, v, e; private final String n;
private final String v;
private final String e;
public Cookie(String name, String value, String expires) { public Cookie(String name, String value, String expires) {
n = name; n = name;
@ -1366,8 +1364,8 @@ public abstract class NanoHTTPD {
* @author LordFokas * @author LordFokas
*/ */
public class CookieHandler implements Iterable<String> { public class CookieHandler implements Iterable<String> {
private ArrayMap<String, String> cookies = new ArrayMap<>(); private final ArrayMap<String, String> cookies = new ArrayMap<>();
private ArrayList<Cookie> queue = new ArrayList<>(); private final ArrayList<Cookie> queue = new ArrayList<>();
public CookieHandler(Map<String, String> httpHeaders) { public CookieHandler(Map<String, String> httpHeaders) {
String raw = httpHeaders.get("cookie"); String raw = httpHeaders.get("cookie");

View File

@ -1,7 +1,7 @@
package de.test.antennapod.util.syndication; package de.test.antennapod.util.syndication;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -9,6 +9,8 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Map; import java.util.Map;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
/** /**
* Test class for FeedDiscoverer * Test class for FeedDiscoverer
*/ */

View File

@ -19,7 +19,7 @@ public class AtomGenerator implements FeedGenerator{
private static final String NS_ATOM = "http://www.w3.org/2005/Atom"; private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
public static final long FEATURE_USE_RFC3339LOCAL = 1; private static final long FEATURE_USE_RFC3339LOCAL = 1;
@Override @Override
public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException { public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException {

View File

@ -1,10 +1,10 @@
package de.test.antennapod.util.syndication.feedgenerator; package de.test.antennapod.util.syndication.feedgenerator;
import de.danoeh.antennapod.core.feed.Feed;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import de.danoeh.antennapod.core.feed.Feed;
/** /**
* Generates a machine-readable, platform-independent representation of a Feed object. * Generates a machine-readable, platform-independent representation of a Feed object.
*/ */
@ -24,5 +24,5 @@ public interface FeedGenerator {
* @param encoding The encoding to use. Must not be null. * @param encoding The encoding to use. Must not be null.
* @param flags Optional argument for enabling implementation-dependent features. * @param flags Optional argument for enabling implementation-dependent features.
*/ */
public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException; void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException;
} }

View File

@ -7,7 +7,7 @@ import java.io.IOException;
/** /**
* Utility methods for FeedGenerator * Utility methods for FeedGenerator
*/ */
public class GeneratorUtil { class GeneratorUtil {
private GeneratorUtil(){} private GeneratorUtil(){}
public static void addPaymentLink(XmlSerializer xml, String paymentLink, boolean withNamespace) throws IOException { public static void addPaymentLink(XmlSerializer xml, String paymentLink, boolean withNamespace) throws IOException {

View File

@ -1,14 +1,16 @@
package de.test.antennapod.util.syndication.feedgenerator; package de.test.antennapod.util.syndication.feedgenerator;
import android.util.Xml; import android.util.Xml;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.util.DateUtils;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.util.DateUtils;
/** /**
* Creates RSS 2.0 feeds. See FeedGenerator for more information. * Creates RSS 2.0 feeds. See FeedGenerator for more information.
*/ */

View File

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

View File

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

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod" package="de.danoeh.antennapod"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="1060402" android:versionCode="1070000"
android:versionName="1.6.4.2"> android:versionName="1.7.0">
<!-- <!--
Version code schema: Version code schema:
"1.2.3-SNAPSHOT" -> 1020300 "1.2.3-SNAPSHOT" -> 1020300
@ -33,11 +33,12 @@
<application <application
android:name="de.danoeh.antennapod.PodcastApp" android:name="de.danoeh.antennapod.PodcastApp"
android:icon="@drawable/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:backupAgent=".core.backup.OpmlBackupAgent" android:backupAgent=".core.backup.OpmlBackupAgent"
android:restoreAnyVersion="true" android:restoreAnyVersion="true"
android:logo="@drawable/ic_launcher"> android:logo="@mipmap/ic_launcher">
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon" <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" /> android:resource="@drawable/ic_notification" />
<meta-data <meta-data
@ -96,15 +97,6 @@
android:name=".activity.DownloadAuthenticationActivity" android:name=".activity.DownloadAuthenticationActivity"
android:launchMode="singleInstance"/> android:launchMode="singleInstance"/>
<activity
android:name=".activity.PreferenceActivityGingerbread"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.danoeh.antennapod.activity.MainActivity"/>
</activity>
<activity <activity
android:name=".activity.PreferenceActivity" android:name=".activity.PreferenceActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
@ -114,16 +106,25 @@
android:value="de.danoeh.antennapod.activity.MainActivity"/> android:value="de.danoeh.antennapod.activity.MainActivity"/>
</activity> </activity>
<activity android:name=".activity.FeedInfoActivity"> <activity
android:name=".activity.FeedInfoActivity"
android:label="@string/feed_info_label">
</activity>
<activity
android:name=".activity.FeedSettingsActivity"
android:windowSoftInputMode="stateHidden"
android:label="@string/feed_settings_label">
</activity> </activity>
<service <service
android:name=".service.PlayerWidgetService" android:name=".core.service.PlayerWidgetJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true" android:enabled="true"
android:exported="false"> android:exported="false">
</service> </service>
<receiver android:name=".receiver.PlayerWidget"> <receiver android:name=".core.receiver.PlayerWidget">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter> </intent-filter>
@ -176,6 +177,13 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/> android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity> </activity>
<activity
android:name=".activity.ImportExportActivity"
android:label="@string/import_export">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity>
<activity <activity
android:name=".activity.OpmlImportFromPathActivity" android:name=".activity.OpmlImportFromPathActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
@ -224,7 +232,8 @@
<activity <activity
android:name=".activity.VideoplayerActivity" android:name=".activity.VideoplayerActivity"
android:configChanges="keyboardHidden|orientation" android:configChanges="keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
android:supportsPictureInPicture="true"
android:screenOrientation="sensorLandscape"> android:screenOrientation="sensorLandscape">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"

View File

@ -0,0 +1,91 @@
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -41,10 +41,8 @@ public class PodcastApp extends Application {
.detectLeakedSqlLiteObjects() .detectLeakedSqlLiteObjects()
.penaltyLog() .penaltyLog()
.penaltyDropBox(); .penaltyDropBox();
if (Build.VERSION.SDK_INT >= 11) { builder.detectActivityLeaks();
builder.detectActivityLeaks(); builder.detectLeakedClosableObjects();
builder.detectLeakedClosableObjects();
}
if(Build.VERSION.SDK_INT >= 16) { if(Build.VERSION.SDK_INT >= 16) {
builder.detectLeakedRegistrationObjects(); builder.detectLeakedRegistrationObjects();
} }

View File

@ -21,8 +21,7 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import rx.Observable; import rx.Single;
import rx.Subscriber;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
@ -34,10 +33,8 @@ public class AboutActivity extends AppCompatActivity {
private static final String TAG = AboutActivity.class.getSimpleName(); private static final String TAG = AboutActivity.class.getSimpleName();
private WebView webview; private WebView webView;
private LinearLayout webviewContainer; private LinearLayout webViewContainer;
private int depth = 0;
private Subscription subscription; private Subscription subscription;
@Override @Override
@ -46,28 +43,25 @@ public class AboutActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.about); setContentView(R.layout.about);
webviewContainer = (LinearLayout) findViewById(R.id.webvContainer); webViewContainer = (LinearLayout) findViewById(R.id.webViewContainer);
webview = (WebView) findViewById(R.id.webvAbout); webView = (WebView) findViewById(R.id.webViewAbout);
webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
if (Build.VERSION.SDK_INT >= 11 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} }
webview.setBackgroundColor(Color.TRANSPARENT); webView.setBackgroundColor(Color.TRANSPARENT);
} }
webview.setWebViewClient(new WebViewClient() { webView.setWebViewClient(new WebViewClient() {
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, String url) { public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.startsWith("http")) { if (!url.startsWith("http")) {
depth++;
return false;
} else {
url = url.replace("file:///android_asset/", ""); url = url.replace("file:///android_asset/", "");
loadAsset(url); loadAsset(url);
return true; return true;
} }
return false;
} }
}); });
@ -75,69 +69,65 @@ public class AboutActivity extends AppCompatActivity {
} }
private void loadAsset(String filename) { private void loadAsset(String filename) {
subscription = Observable.create(new Observable.OnSubscribe<String>() { subscription = Single.create(subscriber -> {
@Override InputStream input = null;
public void call(Subscriber<? super String> subscriber) { try {
InputStream input = null; TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
try { new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background,
TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes( R.attr.about_screen_card_background, R.attr.about_screen_card_border});
new int[] { android.R.attr.textColorPrimary }); String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0));
int colorResource = res.getColor(0, 0); String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0));
String colorString = String.format("#%06X", 0xFFFFFF & colorResource); String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0));
res.recycle(); String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0));
input = getAssets().open(filename); res.recycle();
String webViewData = IOUtils.toString(input, Charset.defaultCharset()); input = getAssets().open(filename);
if(!webViewData.startsWith("<!DOCTYPE html>")) { String webViewData = IOUtils.toString(input, Charset.defaultCharset());
//webViewData = webViewData.replace("\n\n", "</p><p>"); if (!webViewData.startsWith("<!DOCTYPE html>")) {
webViewData = webViewData.replace("%", "&#37;"); webViewData = webViewData.replace("%", "&#37;");
webViewData = webViewData =
"<!DOCTYPE html>" + "<!DOCTYPE html>" +
"<html>" + "<html>" +
"<head>" + "<head>" +
" <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" + " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" +
" <style type=\"text/css\">" + " <style type=\"text/css\">" +
" @font-face {" + " @font-face {" +
" font-family: 'Roboto-Light';" + " font-family: 'Roboto-Light';" +
" src: url('file:///android_asset/Roboto-Light.ttf');" + " src: url('file:///android_asset/Roboto-Light.ttf');" +
" }" + " }" +
" * {" + " * {" +
" color: %s;" + " color: @fontcolor@;" +
" font-family: roboto-Light;" + " font-family: roboto-Light;" +
" font-size: 8pt;" + " font-size: 8pt;" +
" }" + " }" +
" </style>" + " </style>" +
"</head><body><p>" + webViewData + "</p></body></html>"; "</head><body><p>" + webViewData + "</p></body></html>";
webViewData = webViewData.replace("\n", "<br/>"); webViewData = webViewData.replace("\n", "<br/>");
depth++; }
} else { webViewData = webViewData.replace("@fontcolor@", fontColor);
depth = 0; webViewData = webViewData.replace("@background@", backgroundColor);
} webViewData = webViewData.replace("@card_background@", cardBackground);
webViewData = String.format(webViewData, colorString); webViewData = webViewData.replace("@card_border@", cardBorder);
subscriber.onNext(webViewData); subscriber.onSuccess(webViewData);
} catch (IOException e) { } catch (IOException e) {
subscriber.onError(e); Log.e(TAG, Log.getStackTraceString(e));
} finally { subscriber.onError(e);
IOUtils.closeQuietly(input); } finally {
} IOUtils.closeQuietly(input);
subscriber.onCompleted(); }
} })
})
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
webviewData -> webViewData ->
webview.loadDataWithBaseURL("file:///android_asset/", webviewData, "text/html", "utf-8", "about:blank"), webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()),
error -> Log.e(TAG, Log.getStackTraceString(error)) error -> Log.e(TAG, Log.getStackTraceString(error))
); );
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
Log.d(TAG, "depth: " + depth); if (webView.canGoBack()) {
if(depth == 1) { webView.goBack();
loadAsset("about.html");
} else if(depth > 1) {
webview.goBack();
} else { } else {
super.onBackPressed(); super.onBackPressed();
} }
@ -159,9 +149,9 @@ public class AboutActivity extends AppCompatActivity {
if(subscription != null) { if(subscription != null) {
subscription.unsubscribe(); subscription.unsubscribe();
} }
if (webviewContainer != null && webview != null) { if (webViewContainer != null && webView != null) {
webviewContainer.removeAllViews(); webViewContainer.removeAllViews();
webview.destroy(); webView.destroy();
} }
} }
} }

View File

@ -6,41 +6,31 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import java.text.DecimalFormat;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.dialog.VariableSpeedDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/** /**
* Activity for playing audio files. * Activity for playing audio files.
*/ */
public class AudioplayerActivity extends MediaplayerInfoActivity { public class AudioplayerActivity extends MediaplayerInfoActivity {
public static final String TAG = "AudioPlayerActivity"; private static final String TAG = "AudioPlayerActivity";
private AtomicBoolean isSetup = new AtomicBoolean(false); private final AtomicBoolean isSetup = new AtomicBoolean(false);
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent(); playExternalMedia(getIntent(), MediaType.AUDIO);
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
MediaType.AUDIO);
Intent launchIntent = new Intent(this, PlaybackService.class);
launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
true);
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
true);
startService(launchIntent);
} else if (PlaybackService.isCasting()) { } else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this); Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) { if (intent.getComponent() != null &&
!intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
saveCurrentFragment(); saveCurrentFragment();
finish(); finish();
startActivity(intent); startActivity(intent);
@ -95,7 +85,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
UserPreferences.setPlaybackSpeed(String.valueOf(speed)); UserPreferences.setPlaybackSpeed(String.valueOf(speed));
} }
} }
String speedStr = String.format("%.2fx", speed); String speedStr = new DecimalFormat("0.00x").format(speed);
butPlaybackSpeed.setText(speedStr); butPlaybackSpeed.setText(speedStr);
} }

View File

@ -13,9 +13,9 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
* Activity for controlling the remote playback on a Cast device. * Activity for controlling the remote playback on a Cast device.
*/ */
public class CastplayerActivity extends MediaplayerInfoActivity { public class CastplayerActivity extends MediaplayerInfoActivity {
public static final String TAG = "CastPlayerActivity"; private static final String TAG = "CastPlayerActivity";
private AtomicBoolean isSetup = new AtomicBoolean(false); private final AtomicBoolean isSetup = new AtomicBoolean(false);
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {

View File

@ -3,15 +3,14 @@ package de.danoeh.antennapod.activity;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBar;
import android.util.Log; import android.support.v7.app.AppCompatActivity;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadRequest;
@ -23,8 +22,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
* Other arguments are optional. * Other arguments are optional.
* The activity's result will be the same DownloadRequest with the entered username and password. * The activity's result will be the same DownloadRequest with the entered username and password.
*/ */
public class DownloadAuthenticationActivity extends ActionBarActivity { public class DownloadAuthenticationActivity extends AppCompatActivity {
private static final String TAG = "DownloadAuthenticationActivity";
/** /**
* The download request object that contains information about the resource that requires a username and a password * The download request object that contains information about the resource that requires a username and a password
@ -36,47 +34,39 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
*/ */
public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester"; public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester";
public static final String RESULT_REQUEST = "request"; private static final String RESULT_REQUEST = "request";
private EditText etxtUsername; private EditText etxtUsername;
private EditText etxtPassword; private EditText etxtPassword;
private Button butConfirm;
private Button butCancel;
private TextView txtvDescription;
private DownloadRequest request;
private boolean sendToDownloadRequester;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme()); setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getSupportActionBar().hide(); ActionBar actionBar = getSupportActionBar();
setContentView(R.layout.download_authentication_activity); if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.download_authentication_activity);
TextView txtvDescription = (TextView) findViewById(R.id.txtvDescription);
etxtUsername = (EditText) findViewById(R.id.etxtUsername); etxtUsername = (EditText) findViewById(R.id.etxtUsername);
etxtPassword = (EditText) findViewById(R.id.etxtPassword); etxtPassword = (EditText) findViewById(R.id.etxtPassword);
butConfirm = (Button) findViewById(R.id.butConfirm); Button butConfirm = (Button) findViewById(R.id.butConfirm);
butCancel = (Button) findViewById(R.id.butCancel); Button butCancel = (Button) findViewById(R.id.butCancel);
txtvDescription = (TextView) findViewById(R.id.txtvDescription);
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing"); Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
boolean sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST); String newDescription = txtvDescription.getText() + ":\n\n" + request.getTitle();
sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false); txtvDescription.setText(newDescription);
if (savedInstanceState != null) { if (savedInstanceState != null) {
etxtUsername.setText(savedInstanceState.getString("username")); etxtUsername.setText(savedInstanceState.getString("username"));
etxtPassword.setText(savedInstanceState.getString("password")); etxtPassword.setText(savedInstanceState.getString("password"));
} }
txtvDescription.setText(txtvDescription.getText() + ":\n\n" + request.getTitle());
butCancel.setOnClickListener(v -> {
setResult(Activity.RESULT_CANCELED);
finish();
});
butConfirm.setOnClickListener(v -> { butConfirm.setOnClickListener(v -> {
String username = etxtUsername.getText().toString(); String username = etxtUsername.getText().toString();
String password = etxtPassword.getText().toString(); String password = etxtPassword.getText().toString();
@ -87,11 +77,16 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
setResult(Activity.RESULT_OK, result); setResult(Activity.RESULT_OK, result);
if (sendToDownloadRequester) { if (sendToDownloadRequester) {
if (BuildConfig.DEBUG) Log.d(TAG, "Sending request to DownloadRequester");
DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request); DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
} }
finish(); finish();
}); });
butCancel.setOnClickListener(v -> {
setResult(Activity.RESULT_CANCELED);
finish();
});
} }
@Override @Override

View File

@ -2,51 +2,37 @@ package de.danoeh.antennapod.activity;
import android.content.ClipData; import android.content.ClipData;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify; 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.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LangUtils; import de.danoeh.antennapod.core.util.LangUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
@ -59,7 +45,6 @@ public class FeedInfoActivity extends AppCompatActivity {
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedInfoActivity"; private static final String TAG = "FeedInfoActivity";
private boolean autoDeleteChanged = false;
private Feed feed; private Feed feed;
private ImageView imgvCover; private ImageView imgvCover;
@ -70,15 +55,6 @@ public class FeedInfoActivity extends AppCompatActivity {
private TextView lblAuthor; private TextView lblAuthor;
private TextView txtvAuthor; private TextView txtvAuthor;
private TextView txtvUrl; private TextView txtvUrl;
private EditText etxtUsername;
private EditText etxtPassword;
private EditText etxtFilterText;
private RadioButton rdoFilterInclude;
private RadioButton rdoFilterExclude;
private CheckBox cbxAutoDownload;
private CheckBox cbxKeepUpdated;
private Spinner spnAutoDelete;
private boolean filterInclude = true;
private Subscription subscription; private Subscription subscription;
@ -88,56 +64,16 @@ public class FeedInfoActivity extends AppCompatActivity {
public void onClick(View v) { public void onClick(View v) {
if(feed != null && feed.getDownload_url() != null) { if(feed != null && feed.getDownload_url() != null) {
String url = feed.getDownload_url(); String url = feed.getDownload_url();
if (android.os.Build.VERSION.SDK_INT >= 11) { ClipData clipData = ClipData.newPlainText(url, url);
ClipData clipData = ClipData.newPlainText(url, url); android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this
android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this .getSystemService(Context.CLIPBOARD_SERVICE);
.getSystemService(Context.CLIPBOARD_SERVICE); cm.setPrimaryClip(clipData);
cm.setPrimaryClip(clipData);
} else {
android.text.ClipboardManager cm = (android.text.ClipboardManager) FeedInfoActivity.this
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(url);
}
Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT); Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show(); t.show();
} }
} }
}; };
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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme()); setTheme(UserPreferences.getTheme());
@ -148,28 +84,20 @@ public class FeedInfoActivity extends AppCompatActivity {
imgvCover = (ImageView) findViewById(R.id.imgvCover); imgvCover = (ImageView) findViewById(R.id.imgvCover);
txtvTitle = (TextView) findViewById(R.id.txtvTitle); txtvTitle = (TextView) findViewById(R.id.txtvTitle);
TextView txtvAuthorHeader = (TextView) findViewById(R.id.txtvAuthor);
ImageView imgvBackground = (ImageView) findViewById(R.id.imgvBackground);
findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
// https://github.com/bumptech/glide/issues/529
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
txtvDescription = (TextView) findViewById(R.id.txtvDescription); txtvDescription = (TextView) findViewById(R.id.txtvDescription);
lblLanguage = (TextView) findViewById(R.id.lblLanguage); lblLanguage = (TextView) findViewById(R.id.lblLanguage);
txtvLanguage = (TextView) findViewById(R.id.txtvLanguage); txtvLanguage = (TextView) findViewById(R.id.txtvLanguage);
lblAuthor = (TextView) findViewById(R.id.lblAuthor); lblAuthor = (TextView) findViewById(R.id.lblAuthor);
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor); txtvAuthor = (TextView) findViewById(R.id.txtvDetailsAuthor);
txtvUrl = (TextView) findViewById(R.id.txtvUrl); txtvUrl = (TextView) findViewById(R.id.txtvUrl);
cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated);
spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete);
etxtUsername = (EditText) findViewById(R.id.etxtUsername);
etxtPassword = (EditText) findViewById(R.id.etxtPassword);
etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText);
rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include);
rdoFilterInclude.setOnClickListener(v -> {
filterInclude = true;
filterTextChanged = true;
});
rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude);
rdoFilterExclude.setOnClickListener(v -> {
filterInclude = false;
filterTextChanged = true;
});
txtvUrl.setOnClickListener(copyUrlToClipboard); txtvUrl.setOnClickListener(copyUrlToClipboard);
@ -185,7 +113,6 @@ public class FeedInfoActivity extends AppCompatActivity {
Log.d(TAG, "Language is " + feed.getLanguage()); Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor()); Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url()); Log.d(TAG, "URL is " + feed.getDownload_url());
FeedPreferences prefs = feed.getPreferences();
Glide.with(FeedInfoActivity.this) Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation()) .load(feed.getImageLocation())
.placeholder(R.color.light_gray) .placeholder(R.color.light_gray)
@ -194,6 +121,14 @@ public class FeedInfoActivity extends AppCompatActivity {
.fitCenter() .fitCenter()
.dontAnimate() .dontAnimate()
.into(imgvCover); .into(imgvCover);
Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation(FeedInfoActivity.this))
.dontAnimate()
.into(imgvBackground);
txtvTitle.setText(feed.getTitle()); txtvTitle.setText(feed.getTitle());
@ -211,6 +146,7 @@ public class FeedInfoActivity extends AppCompatActivity {
if (!TextUtils.isEmpty(feed.getAuthor())) { if (!TextUtils.isEmpty(feed.getAuthor())) {
txtvAuthor.setText(feed.getAuthor()); txtvAuthor.setText(feed.getAuthor());
txtvAuthorHeader.setText(feed.getAuthor());
} else { } else {
lblAuthor.setVisibility(View.GONE); lblAuthor.setVisibility(View.GONE);
txtvAuthor.setVisibility(View.GONE); txtvAuthor.setVisibility(View.GONE);
@ -224,113 +160,13 @@ public class FeedInfoActivity extends AppCompatActivity {
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}"); 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();
updateAutoDownloadSettings();
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
feed, checked);
dialog.createNewDialog().show();
});
cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
feed.getPreferences().setKeepUpdated(checked);
feed.savePreferences();
});
spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
FeedPreferences.AutoDeleteAction auto_delete_action;
switch (parent.getSelectedItemPosition()) {
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;
}
feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
autoDeleteChanged = true;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Another interface callback
}
});
spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
etxtUsername.setText(prefs.getUsername());
etxtPassword.setText(prefs.getPassword());
etxtUsername.addTextChangedListener(authTextWatcher);
etxtPassword.addTextChangedListener(authTextWatcher);
FeedFilter filter = prefs.getFilter();
if (filter.includeOnly()) {
etxtFilterText.setText(filter.getIncludeFilter());
rdoFilterInclude.setChecked(true);
rdoFilterExclude.setChecked(false);
filterInclude = true;
} else if (filter.excludeOnly()) {
etxtFilterText.setText(filter.getExcludeFilter());
rdoFilterInclude.setChecked(false);
rdoFilterExclude.setChecked(true);
filterInclude = false;
} else {
Log.d(TAG, "No filter set");
rdoFilterInclude.setChecked(false);
rdoFilterExclude.setChecked(false);
etxtFilterText.setText("");
}
etxtFilterText.addTextChangedListener(filterTextWatcher);
supportInvalidateOptionsMenu(); supportInvalidateOptionsMenu();
updateAutoDownloadSettings();
}, error -> { }, error -> {
Log.d(TAG, Log.getStackTraceString(error)); Log.d(TAG, Log.getStackTraceString(error));
finish(); finish();
}); });
} }
@Override
protected void onPause() {
super.onPause();
if (feed != null) {
FeedPreferences prefs = feed.getPreferences();
if (authInfoChanged) {
Log.d(TAG, "Auth info changed, saving credentials");
prefs.setUsername(etxtUsername.getText().toString());
prefs.setPassword(etxtPassword.getText().toString());
}
if (filterTextChanged) {
Log.d(TAG, "Filter info changed, saving...");
String filterText = etxtFilterText.getText().toString();
String includeString = "";
String excludeString = "";
if (filterInclude) {
includeString = filterText;
} else {
excludeString = filterText;
}
prefs.setFilter(new FeedFilter(includeString, excludeString));
}
if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
DBWriter.setFeedPreferences(prefs);
}
authInfoChanged = false;
autoDeleteChanged = false;
filterTextChanged = false;
}
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -375,34 +211,4 @@ public class FeedInfoActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
private void updateAutoDownloadSettings() {
if (feed != null && feed.getPreferences() != null) {
boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
rdoFilterInclude.setEnabled(enabled);
rdoFilterExclude.setEnabled(enabled);
etxtFilterText.setEnabled(enabled);
}
}
private class ApplyToEpisodesDialog extends ConfirmationDialog {
private final Feed feed;
private final 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;
this.autoDownload = autoDownload;
setPositiveText(R.string.yes);
setNegativeText(R.string.no);
}
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
}
}
} }

View File

@ -0,0 +1,370 @@
package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
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.menuhandler.FeedMenuHandler;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Displays information about a feed.
*/
public class FeedSettingsActivity extends AppCompatActivity {
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedSettingsActivity";
private boolean autoDeleteChanged = false;
private Feed feed;
private ImageView imgvCover;
private TextView txtvTitle;
private EditText etxtUsername;
private EditText etxtPassword;
private EditText etxtFilterText;
private RadioButton rdoFilterInclude;
private RadioButton rdoFilterExclude;
private CheckBox cbxAutoDownload;
private CheckBox cbxKeepUpdated;
private Spinner spnAutoDelete;
private boolean filterInclude = true;
private Subscription subscription;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(feed != null && feed.getDownload_url() != null) {
String url = feed.getDownload_url();
ClipData clipData = ClipData.newPlainText(url, url);
android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedSettingsActivity.this
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(clipData);
Toast t = Toast.makeText(FeedSettingsActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
}
}
};
private boolean authInfoChanged = false;
private final 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 final 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());
super.onCreate(savedInstanceState);
setContentView(R.layout.feedsettings);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
imgvCover = (ImageView) findViewById(R.id.imgvCover);
txtvTitle = (TextView) findViewById(R.id.txtvTitle);
TextView txtvAuthorHeader = (TextView) findViewById(R.id.txtvAuthor);
ImageView imgvBackground = (ImageView) findViewById(R.id.imgvBackground);
findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
// https://github.com/bumptech/glide/issues/529
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated);
spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete);
etxtUsername = (EditText) findViewById(R.id.etxtUsername);
etxtPassword = (EditText) findViewById(R.id.etxtPassword);
etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText);
rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include);
rdoFilterInclude.setOnClickListener(v -> {
filterInclude = true;
filterTextChanged = true;
});
rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude);
rdoFilterExclude.setOnClickListener(v -> {
filterInclude = false;
filterTextChanged = true;
});
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;
FeedPreferences prefs = feed.getPreferences();
Glide.with(FeedSettingsActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover);
Glide.with(FeedSettingsActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation(FeedSettingsActivity.this))
.dontAnimate()
.into(imgvBackground);
txtvTitle.setText(feed.getTitle());
if (!TextUtils.isEmpty(feed.getAuthor())) {
txtvAuthorHeader.setText(feed.getAuthor());
}
cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
cbxAutoDownload.setChecked(prefs.getAutoDownload());
cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
feed.getPreferences().setAutoDownload(checked);
feed.savePreferences();
updateAutoDownloadSettings();
ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedSettingsActivity.this,
feed, checked);
dialog.createNewDialog().show();
});
cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
feed.getPreferences().setKeepUpdated(checked);
feed.savePreferences();
});
spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
FeedPreferences.AutoDeleteAction auto_delete_action;
switch (parent.getSelectedItemPosition()) {
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;
}
feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
autoDeleteChanged = true;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Another interface callback
}
});
spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
etxtUsername.setText(prefs.getUsername());
etxtPassword.setText(prefs.getPassword());
etxtUsername.addTextChangedListener(authTextWatcher);
etxtPassword.addTextChangedListener(authTextWatcher);
FeedFilter filter = prefs.getFilter();
if (filter.includeOnly()) {
etxtFilterText.setText(filter.getIncludeFilter());
rdoFilterInclude.setChecked(true);
rdoFilterExclude.setChecked(false);
filterInclude = true;
} else if (filter.excludeOnly()) {
etxtFilterText.setText(filter.getExcludeFilter());
rdoFilterInclude.setChecked(false);
rdoFilterExclude.setChecked(true);
filterInclude = false;
} else {
Log.d(TAG, "No filter set");
rdoFilterInclude.setChecked(false);
rdoFilterExclude.setChecked(false);
etxtFilterText.setText("");
}
etxtFilterText.addTextChangedListener(filterTextWatcher);
supportInvalidateOptionsMenu();
updateAutoDownloadSettings();
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
});
}
@Override
protected void onPause() {
super.onPause();
if (feed != null) {
FeedPreferences prefs = feed.getPreferences();
if (authInfoChanged) {
Log.d(TAG, "Auth info changed, saving credentials");
prefs.setUsername(etxtUsername.getText().toString());
prefs.setPassword(etxtPassword.getText().toString());
}
if (filterTextChanged) {
Log.d(TAG, "Filter info changed, saving...");
String filterText = etxtFilterText.getText().toString();
String includeString = "";
String excludeString = "";
if (filterInclude) {
includeString = filterText;
} else {
excludeString = filterText;
}
prefs.setFilter(new FeedFilter(includeString, excludeString));
}
if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
DBWriter.setFeedPreferences(prefs);
}
authInfoChanged = false;
autoDeleteChanged = false;
filterTextChanged = false;
}
}
@Override
public void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.feedinfo, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(R.id.support_item).setVisible(
feed != null && feed.getPaymentLink() != null);
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
try {
return FeedMenuHandler.onOptionsItemClicked(this, item, feed);
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this,
e.getMessage());
}
return super.onOptionsItemSelected(item);
}
}
private void updateAutoDownloadSettings() {
if (feed != null && feed.getPreferences() != null) {
boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
rdoFilterInclude.setEnabled(enabled);
rdoFilterExclude.setEnabled(enabled);
etxtFilterText.setEnabled(enabled);
}
}
private static class ApplyToEpisodesDialog extends ConfirmationDialog {
private final Feed feed;
private final 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;
this.autoDownload = autoDownload;
setPositiveText(R.string.yes);
setNegativeText(R.string.no);
}
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
}
}
}

View File

@ -4,7 +4,7 @@ package de.danoeh.antennapod.activity;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -18,11 +18,10 @@ import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils; import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
import de.danoeh.antennapod.preferences.PreferenceController;
/** Guides the user through the authentication process */ /** Guides the user through the authentication process */
public class FlattrAuthActivity extends ActionBarActivity { public class FlattrAuthActivity extends AppCompatActivity {
private static final String TAG = "FlattrAuthActivity"; private static final String TAG = "FlattrAuthActivity";
private TextView txtvExplanation; private TextView txtvExplanation;
@ -104,7 +103,7 @@ public class FlattrAuthActivity extends ActionBarActivity {
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
if (authSuccessful) { if (authSuccessful) {
Intent intent = new Intent(this, PreferenceController.getPreferenceActivity()); Intent intent = new Intent(this, PreferenceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent); startActivity(intent);
} else { } else {

View File

@ -0,0 +1,188 @@
package de.danoeh.antennapod.activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.support.design.widget.Snackbar;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
/**
* Displays the 'import/export' screen
*/
public class ImportExportActivity extends AppCompatActivity {
private static final int REQUEST_CODE_RESTORE = 43;
private static final int REQUEST_CODE_BACKUP_DOCUMENT = 44;
private static final String EXPORT_FILENAME = "AntennaPodBackup.db";
private static final String TAG = ImportExportActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowHomeEnabled(true);
}
setContentView(R.layout.import_export_activity);
findViewById(R.id.button_export).setOnClickListener(view -> backup());
findViewById(R.id.button_import).setOnClickListener(view -> restore());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
private void backup() {
if (Build.VERSION.SDK_INT >= 19) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("application/x-sqlite3")
.putExtra(Intent.EXTRA_TITLE, EXPORT_FILENAME);
startActivityForResult(intent, REQUEST_CODE_BACKUP_DOCUMENT);
} else {
try {
File sd = Environment.getExternalStorageDirectory();
File backupDB = new File(sd, EXPORT_FILENAME);
writeBackupTo(new FileOutputStream(backupDB));
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
}
}
}
private void restore() {
if (Build.VERSION.SDK_INT >= 19) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
startActivityForResult(intent, REQUEST_CODE_RESTORE);
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
startActivityForResult(Intent.createChooser(intent,
getString(R.string.import_select_file)), REQUEST_CODE_RESTORE);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode != RESULT_OK || resultData == null) {
return;
}
Uri uri = resultData.getData();
if (requestCode == REQUEST_CODE_RESTORE) {
restoreFrom(uri);
} else if (requestCode == REQUEST_CODE_BACKUP_DOCUMENT) {
backupToDocument(uri);
}
}
private void restoreFrom(Uri inputUri) {
File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
InputStream inputStream = null;
try {
inputStream = getContentResolver().openInputStream(inputUri);
FileUtils.copyInputStreamToFile(inputStream, currentDB);
displayImportSuccessDialog();
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private void displayImportSuccessDialog() {
AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
d.setMessage(R.string.import_ok);
d.setCancelable(false);
d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
ComponentName cn = intent.getComponent();
Intent mainIntent = Intent.makeRestartActivityTask(cn);
startActivity(mainIntent);
});
d.show();
}
private void backupToDocument(Uri uri) {
ParcelFileDescriptor pfd = null;
FileOutputStream fileOutputStream = null;
try {
pfd = getContentResolver().openFileDescriptor(uri, "w");
fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
writeBackupTo(fileOutputStream);
Snackbar.make(findViewById(R.id.import_export_layout),
R.string.export_ok, Snackbar.LENGTH_SHORT).show();
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
} finally {
IOUtils.closeQuietly(fileOutputStream);
if (pfd != null) {
try {
pfd.close();
} catch (IOException e) {
Log.d(TAG, "Unable to close ParcelFileDescriptor");
}
}
}
}
private void writeBackupTo(FileOutputStream outFileStream) {
FileChannel src = null;
FileChannel dst = null;
try {
File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
if (currentDB.exists()) {
src = new FileInputStream(currentDB).getChannel();
dst = outFileStream.getChannel();
dst.transferFrom(src, 0, src.size());
Snackbar.make(findViewById(R.id.import_export_layout),
R.string.export_ok, Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(findViewById(R.id.import_export_layout),
"Can not access current database", Snackbar.LENGTH_SHORT).show();
}
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
} finally {
IOUtils.closeQuietly(src);
IOUtils.closeQuietly(dst);
}
}
}

View File

@ -30,6 +30,9 @@ import android.widget.ListView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -64,7 +67,6 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity; import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
@ -83,7 +85,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_NAME = "MainActivityPrefs";
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag"; private static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
public static final String EXTRA_NAV_TYPE = "nav_type"; public static final String EXTRA_NAV_TYPE = "nav_type";
public static final String EXTRA_NAV_INDEX = "nav_index"; public static final String EXTRA_NAV_INDEX = "nav_index";
@ -91,8 +93,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args"; public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
public static final String EXTRA_FEED_ID = "fragment_feed_id"; public static final String EXTRA_FEED_ID = "fragment_feed_id";
public static final String SAVE_BACKSTACK_COUNT = "backstackCount"; private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
public static final String SAVE_TITLE = "title"; private static final String SAVE_TITLE = "title";
public static final String[] NAV_DRAWER_TAGS = { public static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG, QueueFragment.TAG,
@ -173,7 +175,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
findViewById(R.id.nav_settings).setOnClickListener(v -> { findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer); drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity())); startActivity(new Intent(MainActivity.this, PreferenceActivity.class));
}); });
FragmentTransaction transaction = fm.beginTransaction(); FragmentTransaction transaction = fm.beginTransaction();
@ -201,6 +203,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
transaction.commit(); transaction.commit();
checkFirstLaunch(); checkFirstLaunch();
NotificationUtils.createChannels(this);
UserPreferences.restartUpdateAlarm(false);
} }
private void saveLastNavFragment(String tag) { private void saveLastNavFragment(String tag) {
@ -236,7 +240,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
} }
} }
public void showDrawerPreferencesDialog() { private void showDrawerPreferencesDialog() {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
String[] navLabels = new String[NAV_DRAWER_TAGS.length]; String[] navLabels = new String[NAV_DRAWER_TAGS.length];
final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length];
@ -270,7 +274,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
return (navDrawerData != null) ? navDrawerData.feeds : null; return (navDrawerData != null) ? navDrawerData.feeds : null;
} }
public void loadFragment(int index, Bundle args) { private void loadFragment(int index, Bundle args) {
Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")"); Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")");
if (index < navAdapter.getSubscriptionOffset()) { if (index < navAdapter.getSubscriptionOffset()) {
String tag = navAdapter.getTags().get(index); String tag = navAdapter.getTags().get(index);
@ -399,7 +403,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
} }
} }
private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() { private final AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int viewType = parent.getAdapter().getItemViewType(position); int viewType = parent.getAdapter().getItemViewType(position);
@ -410,7 +414,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
} }
}; };
private AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() { private final AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() {
@Override @Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if(position < navAdapter.getTags().size()) { if(position < navAdapter.getTags().size()) {
@ -573,10 +577,29 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) { switch(item.getItemId()) {
case R.id.mark_all_seen_item: case R.id.mark_all_seen_item:
DBWriter.markFeedSeen(feed.getId()); ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(this,
R.string.mark_all_seen_label,
R.string.mark_all_seen_confirmation_msg) {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
dialog.dismiss();
DBWriter.markFeedSeen(feed.getId());
}
};
markAllSeenConfirmationDialog.createNewDialog().show();
return true; return true;
case R.id.mark_all_read_item: case R.id.mark_all_read_item:
DBWriter.markFeedRead(feed.getId()); ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(this,
R.string.mark_all_read_label,
R.string.mark_all_read_confirmation_msg) {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
dialog.dismiss();
DBWriter.markFeedRead(feed.getId());
}
};
markAllReadConfirmationDialog.createNewDialog().show();
return true; return true;
case R.id.rename_item: case R.id.rename_item:
new RenameFeedDialog(this, feed).show(); new RenameFeedDialog(this, feed).show();
@ -605,8 +628,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
remover.skipOnCompletion = true; remover.skipOnCompletion = true;
int playerStatus = PlaybackPreferences.getCurrentPlayerStatus(); int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) { if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
sendBroadcast(new Intent( IntentUtils.sendLocalBroadcast(MainActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
} }
} }
remover.executeAsync(); remover.executeAsync();
@ -631,7 +653,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private DBReader.NavDrawerData navDrawerData; private DBReader.NavDrawerData navDrawerData;
private int selectedNavListIndex = 0; private int selectedNavListIndex = 0;
private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override @Override
public int getCount() { public int getCount() {
if (navDrawerData != null) { if (navDrawerData != null) {
@ -721,6 +743,15 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
loadData(); loadData();
} }
public void onEventMainThread(ServiceEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
case SERVICE_STARTED:
externalPlayerFragment.connectToPlaybackService();
break;
}
}
public void onEventMainThread(ProgressEvent event) { public void onEventMainThread(ProgressEvent event) {
Log.d(TAG, "onEvent(" + event + ")"); Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) { switch(event.action) {
@ -744,14 +775,12 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
View parentLayout = findViewById(R.id.drawer_layout); View parentLayout = findViewById(R.id.drawer_layout);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if(event.action != null) { if(event.action != null) {
snackbar.setAction(getString(R.string.undo), v -> { snackbar.setAction(getString(R.string.undo), v -> event.action.run());
event.action.run();
});
} }
snackbar.show(); snackbar.show();
} }
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {

View File

@ -1,9 +1,11 @@
package de.danoeh.antennapod.activity; package de.danoeh.antennapod.activity;
import android.Manifest;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
@ -11,6 +13,9 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -33,21 +38,28 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.Locale; import java.util.Locale;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.Supplier; import de.danoeh.antennapod.core.util.Supplier;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError; import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.dialog.SleepTimerDialog; import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import rx.Observable; import rx.Observable;
@ -65,20 +77,21 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static final String TAG = "MediaplayerActivity"; private static final String TAG = "MediaplayerActivity";
private static final String PREFS = "MediaPlayerActivityPreferences"; private static final String PREFS = "MediaPlayerActivityPreferences";
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft"; private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
private static final int REQUEST_CODE_STORAGE = 42;
protected PlaybackController controller; PlaybackController controller;
protected TextView txtvPosition; private TextView txtvPosition;
protected TextView txtvLength; private TextView txtvLength;
protected SeekBar sbPosition; SeekBar sbPosition;
protected ImageButton butRev; private ImageButton butRev;
protected TextView txtvRev; private TextView txtvRev;
protected ImageButton butPlay; private ImageButton butPlay;
protected ImageButton butFF; private ImageButton butFF;
protected TextView txtvFF; private TextView txtvFF;
protected ImageButton butSkip; private ImageButton butSkip;
protected boolean showTimeLeft = false; private boolean showTimeLeft = false;
private boolean isFavorite = false; private boolean isFavorite = false;
@ -183,31 +196,31 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}; };
} }
protected static TextView getTxtvFFFromActivity(MediaplayerActivity activity) { private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF; return activity.txtvFF;
} }
protected static TextView getTxtvRevFromActivity(MediaplayerActivity activity) { private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
return activity.txtvRev; return activity.txtvRev;
} }
protected void onSetSpeedAbilityChanged() { private void onSetSpeedAbilityChanged() {
Log.d(TAG, "onSetSpeedAbilityChanged()"); Log.d(TAG, "onSetSpeedAbilityChanged()");
updatePlaybackSpeedButton(); updatePlaybackSpeedButton();
} }
protected void onPlaybackSpeedChange() { private void onPlaybackSpeedChange() {
updatePlaybackSpeedButtonText(); updatePlaybackSpeedButtonText();
} }
protected void onServiceQueried() { private void onServiceQueried() {
supportInvalidateOptionsMenu(); supportInvalidateOptionsMenu();
} }
protected void chooseTheme() { void chooseTheme() {
setTheme(UserPreferences.getTheme()); setTheme(UserPreferences.getTheme());
} }
protected void setScreenOn(boolean enable) { void setScreenOn(boolean enable) {
} }
@Override @Override
@ -224,9 +237,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override @Override
protected void onPause() { protected void onPause() {
if(controller != null) { if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
controller.reinitServiceIfPaused(); if (controller != null) {
controller.pause(); controller.reinitServiceIfPaused();
controller.pause();
}
} }
super.onPause(); super.onPause();
} }
@ -248,7 +263,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
*/ */
protected abstract void onBufferEnd(); protected abstract void onBufferEnd();
protected void onBufferUpdate(float progress) { private void onBufferUpdate(float progress) {
if (sbPosition != null) { if (sbPosition != null) {
sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax()); sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
} }
@ -257,7 +272,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
/** /**
* Current screen orientation. * Current screen orientation.
*/ */
protected int orientation; private int orientation;
@Override @Override
protected void onStart() { protected void onStart() {
@ -266,6 +281,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release(); controller.release();
} }
controller = newPlaybackController(); controller = newPlaybackController();
setupGUI();
loadMediaInfo();
onPositionObserverUpdate();
} }
@Override @Override
@ -316,11 +334,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
((FeedMedia) media).getItem().getFlattrStatus().flattrable() ((FeedMedia) media).getItem().getFlattrStatus().flattrable()
); );
boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null; boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink); menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
boolean isItemAndHasLink = isFeedMedia && boolean isItemAndHasLink = isFeedMedia &&
((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null; ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink); menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink);
menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink); menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink);
@ -368,7 +386,17 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
MainActivity.class); MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK); | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
View cover = findViewById(R.id.imgvCover);
if (cover != null && Build.VERSION.SDK_INT >= 16) {
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(MediaplayerActivity.this,
cover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
}
finish();
return true; return true;
} else { } else {
if (media != null) { if (media != null) {
@ -546,7 +574,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}); });
break; break;
case R.id.visit_website_item: case R.id.visit_website_item:
Uri uri = Uri.parse(media.getWebsiteLink()); Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
startActivity(new Intent(Intent.ACTION_VIEW, uri)); startActivity(new Intent(Intent.ACTION_VIEW, uri));
break; break;
case R.id.support_item: case R.id.support_item:
@ -589,16 +617,37 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
} }
} }
private static String getWebsiteLinkWithFallback(Playable media) {
if (media == null) {
return null;
} else if (media.getWebsiteLink() != null) {
return media.getWebsiteLink();
} else if (media instanceof FeedMedia) {
return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
}
return null;
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
Log.d(TAG, "onResume()"); Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this); StorageUtils.checkStorageAvailability(this);
if(controller != null) { if (controller != null) {
controller.init(); controller.init();
} }
} }
public void onEventMainThread(ServiceEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
if (event.action == ServiceEvent.Action.SERVICE_STARTED) {
if (controller != null) {
controller.init();
}
}
}
/** /**
* Called by 'handleStatus()' when the PlaybackService is waiting for * Called by 'handleStatus()' when the PlaybackService is waiting for
* a video surface. * a video surface.
@ -609,7 +658,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract void clearStatusMsg(); protected abstract void clearStatusMsg();
protected void onPositionObserverUpdate() { void onPositionObserverUpdate() {
if (controller == null || txtvPosition == null || txtvLength == null) { if (controller == null || txtvPosition == null || txtvLength == null) {
return; return;
} }
@ -645,12 +694,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* to the PlaybackService to ensure that the activity has the right * to the PlaybackService to ensure that the activity has the right
* FeedMedia object. * FeedMedia object.
*/ */
protected boolean loadMediaInfo() { boolean loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()"); Log.d(TAG, "loadMediaInfo()");
if(controller == null || controller.getMedia() == null) { if(controller == null || controller.getMedia() == null) {
return false; return false;
} }
Playable media = controller.getMedia();
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
onPositionObserverUpdate(); onPositionObserverUpdate();
@ -659,18 +707,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true; return true;
} }
protected void updatePlaybackSpeedButton() { void updatePlaybackSpeedButton() {
// Only meaningful on AudioplayerActivity, where it is overridden. // Only meaningful on AudioplayerActivity, where it is overridden.
} }
protected void updatePlaybackSpeedButtonText() { void updatePlaybackSpeedButtonText() {
// Only meaningful on AudioplayerActivity, where it is overridden. // 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). * 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 { public enum SkipDirection {
SKIP_FORWARD( SKIP_FORWARD(
UserPreferences::getFastForwardSecs, UserPreferences::getFastForwardSecs,
MediaplayerActivity::getTxtvFFFromActivity, MediaplayerActivity::getTxtvFFFromActivity,
@ -753,7 +801,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
builder.create().show(); builder.create().show();
} }
protected void setupGUI() { void setupGUI() {
setContentView(getContentViewResourceId()); setContentView(getContentViewResourceId());
sbPosition = (SeekBar) findViewById(R.id.sbPosition); sbPosition = (SeekBar) findViewById(R.id.sbPosition);
txtvPosition = (TextView) findViewById(R.id.txtvPosition); txtvPosition = (TextView) findViewById(R.id.txtvPosition);
@ -823,11 +871,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
} }
if (butSkip != null) { if (butSkip != null) {
butSkip.setOnClickListener(v -> sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE))); butSkip.setOnClickListener(v ->
IntentUtils.sendLocalBroadcast(MediaplayerActivity.this, PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
} }
} }
protected void onRewind() { void onRewind() {
if (controller == null) { if (controller == null) {
return; return;
} }
@ -835,14 +884,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
} }
protected void onPlayPause() { void onPlayPause() {
if(controller == null) { if(controller == null) {
return; return;
} }
controller.init();
controller.playPause(); controller.playPause();
} }
protected void onFastForward() { void onFastForward() {
if (controller == null) { if (controller == null) {
return; return;
} }
@ -852,7 +902,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract int getContentViewResourceId(); protected abstract int getContentViewResourceId();
void handleError(int errorCode) { private void handleError(int errorCode) {
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this); final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
errorDialog.setTitle(R.string.error_label); errorDialog.setTitle(R.string.error_label);
errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode)); errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
@ -865,7 +915,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
errorDialog.create().show(); errorDialog.create().show();
} }
float prog; private float prog;
@Override @Override
public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) { public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
@ -914,4 +964,39 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
} }
} }
void playExternalMedia(Intent intent, MediaType type) {
if (intent == null || intent.getData() == null) {
return;
}
if (Build.VERSION.SDK_INT >= 23
&& ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_STORAGE);
}
return;
}
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type);
new PlaybackServiceStarter(this, media)
.startWhenPrepared(true)
.shouldStream(false)
.prepareImmediately(true)
.start();
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (requestCode == REQUEST_CODE_STORAGE) {
if (grantResults.length <= 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
}
}
}
} }

View File

@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -47,6 +48,7 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog;
@ -60,7 +62,6 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity; import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
@ -72,16 +73,17 @@ import rx.schedulers.Schedulers;
*/ */
public abstract class MediaplayerInfoActivity extends MediaplayerActivity implements NavDrawerActivity { public abstract class MediaplayerInfoActivity extends MediaplayerActivity implements NavDrawerActivity {
private static final String TAG = "MediaplayerInfoActivity";
private static final int POS_COVER = 0; private static final int POS_COVER = 0;
private static final int POS_DESCR = 1; private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2; private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3; private static final int NUM_CONTENT_FRAGMENTS = 3;
final String TAG = "MediaplayerInfoActivity";
private static final String PREFS = "AudioPlayerActivityPreferences"; private static final String PREFS = "AudioPlayerActivityPreferences";
private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition";
public static final String[] NAV_DRAWER_TAGS = { private static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG, QueueFragment.TAG,
EpisodesFragment.TAG, EpisodesFragment.TAG,
SubscriptionFragment.TAG, SubscriptionFragment.TAG,
@ -91,8 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG NavListAdapter.SUBSCRIPTION_LIST_TAG
}; };
protected Button butPlaybackSpeed; Button butPlaybackSpeed;
protected ImageButton butCastDisconnect; ImageButton butCastDisconnect;
private DrawerLayout drawerLayout; private DrawerLayout drawerLayout;
private NavListAdapter navAdapter; private NavListAdapter navAdapter;
private ListView navList; private ListView navList;
@ -112,6 +114,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
} }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportPostponeEnterTransition();
}
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
@ -145,7 +153,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
setTheme(UserPreferences.getNoTitleTheme()); setTheme(UserPreferences.getNoTitleTheme());
} }
protected void saveCurrentFragment() { void saveCurrentFragment() {
if(pager == null) { if(pager == null) {
return; return;
} }
@ -153,7 +161,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
prefs.edit() prefs.edit()
.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem()) .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem())
.commit(); .apply();
} }
@Override @Override
@ -235,7 +243,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
drawerToggle.setDrawerIndicatorEnabled(false); drawerToggle.setDrawerIndicatorEnabled(false);
drawerLayout.setDrawerListener(drawerToggle); drawerLayout.addDrawerListener(drawerToggle);
navAdapter = new NavListAdapter(itemAccess, this); navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter); navList.setAdapter(navAdapter);
@ -263,7 +271,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
findViewById(R.id.nav_settings).setOnClickListener(v -> { findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer); drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceController.getPreferenceActivity())); startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class));
}); });
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
@ -277,6 +285,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pageIndicator.setViewPager(pager); pageIndicator.setViewPager(pager);
loadLastFragment(); loadLastFragment();
pager.onSaveInstanceState(); pager.onSaveInstanceState();
navList.post(this::supportStartPostponedEnterTransition);
} }
@Override @Override
@ -297,7 +307,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
return true; return true;
} }
public void notifyMediaPositionChanged() { private void notifyMediaPositionChanged() {
if(pagerAdapter == null) { if(pagerAdapter == null) {
return; return;
} }
@ -387,12 +397,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
new RenameFeedDialog(this, feed).show(); new RenameFeedDialog(this, feed).show();
return true; return true;
case R.id.remove_item: case R.id.remove_item:
final FeedRemover remover = new FeedRemover(this, feed) { final FeedRemover remover = new FeedRemover(this, feed);
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
};
ConfirmationDialog conDialog = new ConfirmationDialog(this, ConfirmationDialog conDialog = new ConfirmationDialog(this,
R.string.remove_feed_label, R.string.remove_feed_label,
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) { getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@ -404,12 +409,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
Playable playable = controller.getMedia(); Playable playable = controller.getMedia();
if (playable != null && playable instanceof FeedMedia) { if (playable != null && playable instanceof FeedMedia) {
FeedMedia media = (FeedMedia) playable; FeedMedia media = (FeedMedia) playable;
if (media.getItem().getFeed().getId() == feed.getId()) { if (media.getItem() != null && media.getItem().getFeed() != null &&
media.getItem().getFeed().getId() == feed.getId()) {
Log.d(TAG, "Currently playing episode is about to be deleted, skipping"); Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
remover.skipOnCompletion = true; remover.skipOnCompletion = true;
if(controller.getStatus() == PlayerStatus.PLAYING) { if(controller.getStatus() == PlayerStatus.PLAYING) {
sendBroadcast(new Intent( IntentUtils.sendLocalBroadcast(MediaplayerInfoActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
} }
} }
} }
@ -438,7 +443,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
} }
} }
public void showDrawerPreferencesDialog() { private void showDrawerPreferencesDialog() {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
String[] navLabels = new String[NAV_DRAWER_TAGS.length]; String[] navLabels = new String[NAV_DRAWER_TAGS.length];
final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length];
@ -483,14 +488,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
View parentLayout = findViewById(R.id.drawer_layout); View parentLayout = findViewById(R.id.drawer_layout);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if (event.action != null) { if (event.action != null) {
snackbar.setAction(getString(R.string.undo), v -> { snackbar.setAction(getString(R.string.undo), v -> event.action.run());
event.action.run();
});
} }
snackbar.show(); snackbar.show();
} }
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {

View File

@ -5,8 +5,9 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper; import android.support.annotation.UiThread;
import android.support.v4.app.NavUtils; import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils; import android.text.TextUtils;
@ -55,7 +56,6 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler; import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException; import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator; import de.danoeh.antennapod.core.util.FileNameGenerator;
@ -66,7 +66,6 @@ import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import rx.Observable; import rx.Observable;
import rx.Subscriber;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
@ -84,7 +83,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_FEEDURL = "arg.feedurl"; public static final String ARG_FEEDURL = "arg.feedurl";
// Optional argument: specify a title for the actionbar. // Optional argument: specify a title for the actionbar.
public static final String ARG_TITLE = "title"; public static final String ARG_TITLE = "title";
public static final int RESULT_ERROR = 2; private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity"; private static final String TAG = "OnlineFeedViewActivity";
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE; private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds; private volatile List<Feed> feeds;
@ -101,7 +100,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private Subscription download; private Subscription download;
private Subscription parser; private Subscription parser;
private Subscription updater; private Subscription updater;
private EventDistributor.EventListener listener = new EventDistributor.EventListener() { private final EventDistributor.EventListener listener = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) { if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
@ -112,9 +111,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
feeds -> { feeds -> {
OnlineFeedViewActivity.this.feeds = feeds; OnlineFeedViewActivity.this.feeds = feeds;
setSubscribeButtonState(feed); setSubscribeButtonState(feed);
}, error -> { }, error -> Log.e(TAG, Log.getStackTraceString(error))
Log.e(TAG, Log.getStackTraceString(error));
}
); );
} else if ((arg & EVENTS) != 0) { } else if ((arg & EVENTS) != 0) {
setSubscribeButtonState(feed); setSubscribeButtonState(feed);
@ -131,10 +128,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme()); setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (getIntent() != null && getIntent().hasExtra(ARG_TITLE)) { if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
getSupportActionBar().setTitle(getIntent().getStringExtra(ARG_TITLE)); actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE));
} }
StorageUtils.checkStorageAvailability(this); StorageUtils.checkStorageAvailability(this);
@ -146,7 +146,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { || TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)) feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString(); ? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
getSupportActionBar().setTitle(R.string.add_feed_label); if (actionBar != null) {
actionBar.setTitle(R.string.add_feed_label);
}
} else { } else {
throw new IllegalArgumentException("Activity must be started with feedurl argument!"); throw new IllegalArgumentException("Activity must be started with feedurl argument!");
} }
@ -265,16 +267,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password, feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
true, null); true, null);
download = Observable.create(new Observable.OnSubscribe<DownloadStatus>() { download = Observable.fromCallable(() -> {
@Override feeds = DBReader.getFeedList();
public void call(Subscriber<? super DownloadStatus> subscriber) { downloader = new HttpDownloader(request);
feeds = DBReader.getFeedList(); downloader.call();
downloader = new HttpDownloader(request); return downloader.getResult();
downloader.call();
Log.d(TAG, "Download was completed");
subscriber.onNext(downloader.getResult());
subscriber.onCompleted();
}
}) })
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -286,6 +283,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (status == null) { if (status == null) {
Log.wtf(TAG, "DownloadStatus returned by Downloader was null"); Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
finish(); finish();
return;
} }
if (status.isCancelled()) { if (status.isCancelled()) {
return; return;
@ -300,7 +298,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
} }
} else { } else {
String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this); String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
if (errorMsg != null && status.getReasonDetailed() != null) { if (status.getReasonDetailed() != null) {
errorMsg += " (" + status.getReasonDetailed() + ")"; errorMsg += " (" + status.getReasonDetailed() + ")";
} }
showErrorDialog(errorMsg); showErrorDialog(errorMsg);
@ -313,39 +311,38 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
} }
Log.d(TAG, "Parsing feed"); Log.d(TAG, "Parsing feed");
parser = Observable.create(new Observable.OnSubscribe<FeedHandlerResult>() { parser = Observable.fromCallable(() -> {
@Override FeedHandler handler = new FeedHandler();
public void call(Subscriber<? super FeedHandlerResult> subscriber) { try {
FeedHandler handler = new FeedHandler(); return handler.parseFeed(feed);
try { } catch (UnsupportedFeedtypeException e) {
FeedHandlerResult result = handler.parseFeed(feed); Log.d(TAG, "Unsupported feed type detected");
subscriber.onNext(result); if ("html".equalsIgnoreCase(e.getRootElement())) {
} catch (UnsupportedFeedtypeException e) { showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
Log.d(TAG, "Unsupported feed type detected"); return null;
if (TextUtils.equals("html", e.getRootElement().toLowerCase())) { } else {
showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url()); throw e;
} else {
subscriber.onError(e);
}
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
subscriber.onError(e);
} finally {
boolean rc = new File(feed.getFile_url()).delete();
Log.d(TAG, "Deleted feed source file. Result: " + rc);
subscriber.onCompleted();
} }
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw e;
} finally {
boolean rc = new File(feed.getFile_url()).delete();
Log.d(TAG, "Deleted feed source file. Result: " + rc);
} }
}) })
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> { .subscribe(result -> {
beforeShowFeedInformation(result.feed); if(result != null) {
showFeedInformation(result.feed, result.alternateFeedUrls); beforeShowFeedInformation(result.feed);
showFeedInformation(result.feed, result.alternateFeedUrls);
}
}, error -> { }, error -> {
String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString( String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")"; OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
showErrorDialog(errorMsg); showErrorDialog(errorMsg);
Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
}); });
} }
@ -383,8 +380,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
this.selectedDownloadUrl = feed.getDownload_url(); this.selectedDownloadUrl = feed.getDownload_url();
EventDistributor.getInstance().register(listener); EventDistributor.getInstance().register(listener);
ListView listView = (ListView) findViewById(R.id.listview); ListView listView = (ListView) findViewById(R.id.listview);
LayoutInflater inflater = (LayoutInflater) LayoutInflater inflater = LayoutInflater.from(this);
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false); View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
listView.addHeaderView(header); listView.addHeaderView(header);
@ -398,9 +394,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
subscribeButton = (Button) header.findViewById(R.id.butSubscribe); subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) { if (StringUtils.isNotBlank(feed.getImageUrl())) {
Glide.with(this) Glide.with(this)
.load(feed.getImage().getDownload_url()) .load(feed.getImageUrl())
.placeholder(R.color.light_gray) .placeholder(R.color.light_gray)
.error(R.color.light_gray) .error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
@ -414,7 +410,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
description.setText(feed.getDescription()); description.setText(feed.getDescription());
subscribeButton.setOnClickListener(v -> { subscribeButton.setOnClickListener(v -> {
if(feed != null && feedInFeedlist(feed)) { if(feedInFeedlist(feed)) {
Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class); Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class);
// feed.getId() is always 0, we have to retrieve the id from the feed list from // feed.getId() is always 0, we have to retrieve the id from the feed list from
// the database // the database
@ -508,8 +504,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
return 0; return 0;
} }
@UiThread
private void showErrorDialog(String errorMsg) { private void showErrorDialog(String errorMsg) {
assert(Looper.myLooper() == Looper.getMainLooper()); // run on UI thread
if (!isFinishing() && !isPaused) { if (!isFinishing() && !isPaused) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.error_label); builder.setTitle(R.string.error_label);
@ -586,7 +582,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private class FeedViewAuthenticationDialog extends AuthenticationDialog { private class FeedViewAuthenticationDialog extends AuthenticationDialog {
private String feedUrl; private final String feedUrl;
FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) { FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
super(context, titleRes, true, false, null, null); super(context, titleRes, true, false, null, null);

View File

@ -68,7 +68,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
} }
} }
protected void importUri(@Nullable Uri uri) { void importUri(@Nullable Uri uri) {
if(uri == null) { if(uri == null) {
new MaterialDialog.Builder(this) new MaterialDialog.Builder(this)
.content(R.string.opml_import_error_no_file) .content(R.string.opml_import_error_no_file)
@ -114,7 +114,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
} }
/** Starts the import process. */ /** Starts the import process. */
protected void startImport() { private void startImport() {
try { try {
Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8); Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
importWorker = new OpmlImportWorker(this, mReader) { importWorker = new OpmlImportWorker(this, mReader) {
@ -144,7 +144,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
} }
} }
protected boolean finishWhenCanceled() { boolean finishWhenCanceled() {
return false; return false;
} }

View File

@ -1,14 +1,12 @@
package de.danoeh.antennapod.activity; package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -16,6 +14,9 @@ import android.widget.FrameLayout;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import com.bytehamster.lib.preferencesearch.SearchPreference;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.PreferenceController; import de.danoeh.antennapod.preferences.PreferenceController;
@ -24,25 +25,40 @@ import de.danoeh.antennapod.preferences.PreferenceController;
* PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see * PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see
* PreferenceController. * PreferenceController.
*/ */
public class PreferenceActivity extends AppCompatActivity { public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener {
public static final String PARAM_RESOURCE = "resource";
private static WeakReference<PreferenceActivity> instance; private static WeakReference<PreferenceActivity> instance;
private PreferenceController preferenceController; private PreferenceController preferenceController;
private MainFragment prefFragment;
private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() { private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB) private PreferenceFragmentCompat fragment;
@Override @Override
public Preference findPreference(CharSequence key) { public void setFragment(PreferenceFragmentCompat fragment) {
return prefFragment.findPreference(key); this.fragment = fragment;
} }
@Override @Override
public Activity getActivity() { public PreferenceFragmentCompat getFragment() {
return fragment;
}
@Override
public Preference findPreference(CharSequence key) {
return fragment.findPreference(key);
}
@Override
public PreferenceScreen getPreferenceScreen() {
return fragment.getPreferenceScreen();
}
@Override
public AppCompatActivity getActivity() {
return PreferenceActivity.this; return PreferenceActivity.this;
} }
}; };
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
// This must be the FIRST thing we do, otherwise other code may not have the // This must be the FIRST thing we do, otherwise other code may not have the
@ -68,8 +84,21 @@ public class PreferenceActivity extends AppCompatActivity {
// since the MainFragment depends on the preferenceController already being created // since the MainFragment depends on the preferenceController already being created
preferenceController = new PreferenceController(preferenceUI); preferenceController = new PreferenceController(preferenceUI);
prefFragment = new MainFragment(); showPreferenceScreen(R.xml.preferences, false);
getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit(); }
private void showPreferenceScreen(int screen, boolean addHistory) {
PreferenceFragmentCompat prefFragment = new MainFragment();
preferenceUI.setFragment(prefFragment);
Bundle args = new Bundle();
args.putInt(PARAM_RESOURCE, screen);
prefFragment.setArguments(args);
if (addHistory) {
getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment)
.addToBackStack(getString(PreferenceController.getTitleOfPage(screen))).commit();
} else {
getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
}
} }
@Override @Override
@ -88,24 +117,40 @@ public class PreferenceActivity extends AppCompatActivity {
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
finish(); if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else {
getSupportFragmentManager().popBackStack();
}
return true; return true;
default: default:
return false; return false;
} }
} }
@TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override
public static class MainFragment extends PreferenceFragment { public void onSearchResultClicked(SearchPreferenceResult result) {
showPreferenceScreen(result.getResourceFile(), true);
result.highlight(preferenceUI.getFragment());
}
public static class MainFragment extends PreferenceFragmentCompat {
private int screen;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setRetainInstance(true); setRetainInstance(true);
addPreferencesFromResource(R.xml.preferences); }
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
screen = getArguments().getInt(PARAM_RESOURCE);
addPreferencesFromResource(screen);
PreferenceActivity activity = instance.get(); PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) { if (activity != null && activity.preferenceController != null) {
activity.preferenceController.onCreate(); activity.preferenceUI.setFragment(this);
activity.preferenceController.onCreate(screen);
} }
} }
@ -114,15 +159,17 @@ public class PreferenceActivity extends AppCompatActivity {
super.onResume(); super.onResume();
PreferenceActivity activity = instance.get(); PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) { if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onResume(); activity.setTitle(PreferenceController.getTitleOfPage(screen));
activity.preferenceUI.setFragment(this);
activity.preferenceController.onResume(screen);
} }
} }
@Override @Override
public void onPause() { public void onPause() {
PreferenceActivity activity = instance.get(); PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) { if (screen == R.xml.preferences_gpodder) {
activity.preferenceController.onPause(); activity.preferenceController.unregisterGpodnet();
} }
super.onPause(); super.onPause();
} }
@ -130,8 +177,8 @@ public class PreferenceActivity extends AppCompatActivity {
@Override @Override
public void onStop() { public void onStop() {
PreferenceActivity activity = instance.get(); PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) { if (screen == R.xml.preferences_storage) {
activity.preferenceController.onStop(); activity.preferenceController.unsubscribeExportSubscription();
} }
super.onStop(); super.onStop();
} }

View File

@ -1,97 +0,0 @@
package de.danoeh.antennapod.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources.Theme;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.PreferenceController;
/**
* PreferenceActivity for API 10. In order to change the behavior of the preference UI, see
* PreferenceController.
*/
public class PreferenceActivityGingerbread extends android.preference.PreferenceActivity {
private static final String TAG = "PreferenceActivity";
private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
@SuppressWarnings("deprecation")
@Override
public Preference findPreference(CharSequence key) {
return PreferenceActivityGingerbread.this.findPreference(key);
}
@Override
public Activity getActivity() {
return PreferenceActivityGingerbread.this;
}
};
private PreferenceController preferenceController;
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
preferenceController = new PreferenceController(preferenceUI);
preferenceController.onCreate();
}
@Override
protected void onResume() {
super.onResume();
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);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
preferenceController.onActivityResult(requestCode, resultCode, data);
}
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
super.onPreferenceTreeClick(preferenceScreen, preference);
if (preference != null)
if (preference instanceof PreferenceScreen)
if (((PreferenceScreen) preference).getDialog() != null)
((PreferenceScreen) preference)
.getDialog()
.getWindow()
.getDecorView()
.setBackgroundDrawable(
this.getWindow().getDecorView()
.getBackground().getConstantState()
.newDrawable()
);
return false;
}
}

View File

@ -1,23 +1,50 @@
package de.danoeh.antennapod.activity; package de.danoeh.antennapod.activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.widget.ProgressBar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import rx.Completable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/** /**
* Creator: vbarad * Shows the AntennaPod logo while waiting for the main activity to start
* Date: 2016-12-03
* Project: AntennaPod
*/ */
public class SplashActivity extends AppCompatActivity { public class SplashActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
Intent intent = new Intent(this, MainActivity.class); ProgressBar progressBar = findViewById(R.id.progressBar);
startActivity(intent); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
finish(); Drawable wrapDrawable = DrawableCompat.wrap(progressBar.getIndeterminateDrawable());
} DrawableCompat.setTint(wrapDrawable, 0xffffffff);
progressBar.setIndeterminateDrawable(DrawableCompat.unwrap(wrapDrawable));
} else {
progressBar.getIndeterminateDrawable().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN);
}
Completable.create(subscriber -> {
// Trigger schema updates
PodDBAdapter.getInstance().open();
PodDBAdapter.getInstance().close();
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
});
}
} }

View File

@ -164,7 +164,7 @@ public class StorageErrorActivity extends AppCompatActivity {
startActivity(new Intent(this, MainActivity.class)); startActivity(new Intent(this, MainActivity.class));
} }
private BroadcastReceiver mediaUpdate = new BroadcastReceiver() { private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {

View File

@ -7,29 +7,34 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.v4.view.WindowCompat; import android.support.v4.view.WindowCompat;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.SeekBar; import android.widget.SeekBar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
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.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.view.AspectRatioVideoView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.view.AspectRatioVideoView;
/** /**
* Activity for playing video files. * Activity for playing video files.
*/ */
@ -45,12 +50,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
private VideoControlsHider videoControlsHider = new VideoControlsHider(this); private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
private AtomicBoolean isSetup = new AtomicBoolean(false); private final AtomicBoolean isSetup = new AtomicBoolean(false);
private LinearLayout controls; private LinearLayout controls;
private LinearLayout videoOverlay; private LinearLayout videoOverlay;
private AspectRatioVideoView videoview; private AspectRatioVideoView videoview;
private ProgressBar progressIndicator; private ProgressBar progressIndicator;
private FrameLayout videoframe;
@Override @Override
protected void chooseTheme() { protected void chooseTheme() {
@ -70,20 +76,8 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (getIntent().getAction() != null if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) { playExternalMedia(getIntent(), MediaType.VIDEO);
Intent intent = getIntent();
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
MediaType.VIDEO);
Intent launchIntent = new Intent(this, PlaybackService.class);
launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
true);
launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
true);
startService(launchIntent);
} else if (PlaybackService.isCasting()) { } else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this); Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
@ -94,11 +88,28 @@ public class VideoplayerActivity extends MediaplayerActivity {
} }
} }
@Override
protected void onStop() {
super.onStop();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
videoControlsHider.stop();
}
}
@Override
public void onUserLeaveHint () {
if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior()
== UserPreferences.VideoBackgroundBehavior.PICTURE_IN_PICTURE) {
compatEnterPictureInPicture();
}
}
@Override @Override
protected void onPause() { protected void onPause() {
videoControlsHider.stop(); if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
controller.pause(); controller.pause();
}
} }
super.onPause(); super.onPause();
} }
@ -126,7 +137,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override @Override
protected void setupGUI() { protected void setupGUI() {
if(isSetup.getAndSet(true)) { if (isSetup.getAndSet(true)) {
return; return;
} }
super.setupGUI(); super.setupGUI();
@ -134,34 +145,30 @@ public class VideoplayerActivity extends MediaplayerActivity {
controls = (LinearLayout) findViewById(R.id.controls); controls = (LinearLayout) findViewById(R.id.controls);
videoOverlay = (LinearLayout) findViewById(R.id.overlay); videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (AspectRatioVideoView) findViewById(R.id.videoview); videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
videoframe = (FrameLayout) findViewById(R.id.videoframe);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator); progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
videoview.getHolder().addCallback(surfaceHolderCallback); videoview.getHolder().addCallback(surfaceHolderCallback);
videoview.setOnTouchListener(onVideoviewTouched); videoframe.setOnTouchListener(onVideoviewTouched);
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
if (Build.VERSION.SDK_INT >= 16) { if (Build.VERSION.SDK_INT >= 16) {
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
} }
if (Build.VERSION.SDK_INT >= 14) { videoOverlay.setFitsSystemWindows(true);
videoOverlay.setFitsSystemWindows(true);
}
setupVideoControlsToggler(); setupVideoControlsToggler();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); WindowManager.LayoutParams.FLAG_FULLSCREEN);
videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() ->
videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight()));
} }
@Override @Override
protected void onAwaitingVideoSurface() { protected void onAwaitingVideoSurface() {
setupVideoAspectRatio();
if (videoSurfaceCreated && controller != null) { if (videoSurfaceCreated && controller != null) {
Log.d(TAG, "Videosurface already created, setting videosurface now"); Log.d(TAG, "Videosurface already created, setting videosurface now");
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
}
controller.setVideoSurface(videoview.getHolder()); controller.setVideoSurface(videoview.getHolder());
} }
} }
@ -180,8 +187,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE); progressIndicator.setVisibility(View.INVISIBLE);
} }
View.OnTouchListener onVideoviewTouched = (v, event) -> { private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) { if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
return true;
}
videoControlsHider.stop(); videoControlsHider.stop();
toggleVideoControlsVisibility(); toggleVideoControlsVisibility();
if (videoControlsShowing) { if (videoControlsShowing) {
@ -194,11 +204,23 @@ public class VideoplayerActivity extends MediaplayerActivity {
}; };
@SuppressLint("NewApi") @SuppressLint("NewApi")
void setupVideoControlsToggler() { private void setupVideoControlsToggler() {
videoControlsHider.stop(); videoControlsHider.stop();
videoControlsHider.start(); videoControlsHider.start();
} }
private void setupVideoAspectRatio() {
if (videoSurfaceCreated && controller != null) {
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
}
}
}
private void toggleVideoControlsVisibility() { private void toggleVideoControlsVisibility() {
if (videoControlsShowing) { if (videoControlsShowing) {
getSupportActionBar().hide(); getSupportActionBar().hide();
@ -247,14 +269,16 @@ public class VideoplayerActivity extends MediaplayerActivity {
Log.e(TAG, "Couldn't attach surface to mediaplayer - reference to service was null"); Log.e(TAG, "Couldn't attach surface to mediaplayer - reference to service was null");
} }
} }
setupVideoAspectRatio();
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Videosurface was destroyed"); Log.d(TAG, "Videosurface was destroyed");
videoSurfaceCreated = false; videoSurfaceCreated = false;
if (controller != null && !destroyingDueToReload) { if (controller != null && !destroyingDueToReload
&& UserPreferences.getVideoBackgroundBehavior()
!= UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
controller.notifyVideoSurfaceAbandoned(); controller.notifyVideoSurfaceAbandoned();
} }
} }
@ -263,6 +287,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override @Override
protected void onReloadNotification(int notificationCode) { protected void onReloadNotification(int notificationCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO
|| notificationCode == PlaybackService.EXTRA_CODE_CAST) {
finish();
}
return;
}
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) { if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
Log.d(TAG, "ReloadNotification received, switching to Audioplayer now"); Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
destroyingDueToReload = true; destroyingDueToReload = true;
@ -307,28 +338,31 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoOverlay.startAnimation(animation); videoOverlay.startAnimation(animation);
controls.startAnimation(animation); controls.startAnimation(animation);
} }
if (Build.VERSION.SDK_INT >= 14) { videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void hideVideoControls() { private void hideVideoControls(boolean showAnimation) {
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out); if (showAnimation) {
if (animation != null) { final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
videoOverlay.startAnimation(animation); if (animation != null) {
controls.startAnimation(animation); videoOverlay.startAnimation(animation);
} controls.startAnimation(animation);
if (Build.VERSION.SDK_INT >= 14) { }
int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag);
videoOverlay.setFitsSystemWindows(true);
} }
int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag);
videoOverlay.setFitsSystemWindows(true);
videoOverlay.setVisibility(View.GONE); videoOverlay.setVisibility(View.GONE);
controls.setVisibility(View.GONE); controls.setVisibility(View.GONE);
} }
private void hideVideoControls() {
hideVideoControls(true);
}
@Override @Override
protected int getContentViewResourceId() { protected int getContentViewResourceId() {
return R.layout.videoplayer_activity; return R.layout.videoplayer_activity;
@ -344,24 +378,53 @@ public class VideoplayerActivity extends MediaplayerActivity {
} }
} }
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (PictureInPictureUtil.supportsPictureInPicture(this)) {
menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.player_go_to_picture_in_picture) {
compatEnterPictureInPicture();
return true;
}
return super.onOptionsItemSelected(item);
}
private void compatEnterPictureInPicture() {
if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
getSupportActionBar().hide();
hideVideoControls(false);
enterPictureInPictureMode();
}
}
private static class VideoControlsHider extends Handler { private static class VideoControlsHider extends Handler {
private static final int DELAY = 2500; private static final int DELAY = 2500;
private WeakReference<VideoplayerActivity> activity; private WeakReference<VideoplayerActivity> activity;
public VideoControlsHider(VideoplayerActivity activity) { VideoControlsHider(VideoplayerActivity activity) {
this.activity = new WeakReference<>(activity); this.activity = new WeakReference<>(activity);
} }
private final Runnable hideVideoControls = () -> { private final Runnable hideVideoControls = () -> {
VideoplayerActivity vpa = activity.get(); VideoplayerActivity vpa = activity != null ? activity.get() : null;
if(vpa == null) { if (vpa == null) {
return; return;
} }
if (vpa.videoControlsShowing) { if (vpa.videoControlsShowing) {
Log.d(TAG, "Hiding video controls"); Log.d(TAG, "Hiding video controls");
vpa.getSupportActionBar().hide(); ActionBar actionBar = vpa.getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
vpa.hideVideoControls(); vpa.hideVideoControls();
vpa.videoControlsShowing = false; vpa.videoControlsShowing = false;
} }
@ -371,7 +434,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
this.postDelayed(hideVideoControls, DELAY); this.postDelayed(hideVideoControls, DELAY);
} }
public void stop() { void stop() {
this.removeCallbacks(hideVideoControls); this.removeCallbacks(hideVideoControls);
} }

View File

@ -61,7 +61,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
private volatile String password; private volatile String password;
private volatile GpodnetDevice selectedDevice; private volatile GpodnetDevice selectedDevice;
View[] views; private View[] views;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -171,11 +171,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
return null; return null;
} }
}; };
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
} else {
authTask.execute();
}
} }
}); });
} }

View File

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

View File

@ -16,7 +16,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
* Utility methods for the action button that is displayed on the right hand side * Utility methods for the action button that is displayed on the right hand side
* of a listitem. * of a listitem.
*/ */
public class ActionButtonUtils { class ActionButtonUtils {
private final int[] labels; private final int[] labels;
private final TypedArray drawables; private final TypedArray drawables;

View File

@ -21,7 +21,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.Iconify;
import com.nineoldandroids.view.ViewHelper;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -30,12 +29,12 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment; import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@ -68,11 +67,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
this.actionButtonCallback = actionButtonCallback; this.actionButtonCallback = actionButtonCallback;
this.showOnlyNewEpisodes = showOnlyNewEpisodes; this.showOnlyNewEpisodes = showOnlyNewEpisodes;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent); normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
} }
@ -101,7 +96,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration); holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration);
holder.item = null; holder.item = null;
holder.mainActivityRef = mainActivityRef; holder.mainActivityRef = mainActivityRef;
holder.position = -1;
// so we can grab this later // so we can grab this later
view.setTag(holder); view.setTag(holder);
@ -113,11 +107,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
final FeedItem item = itemAccess.getItem(position); final FeedItem item = itemAccess.getItem(position);
if (item == null) return; if (item == null) return;
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
this.position = position; this.position = holder.getAdapterPosition();
return false; return false;
}); });
holder.item = item; holder.item = item;
holder.position = position;
holder.placeholder.setVisibility(View.VISIBLE); holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle()); holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle()); holder.title.setText(item.getTitle());
@ -129,9 +122,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.statusUnread.setVisibility(View.VISIBLE); holder.statusUnread.setVisibility(View.VISIBLE);
} }
if(item.isPlayed()) { if(item.isPlayed()) {
ViewHelper.setAlpha(holder.content, 0.5f); holder.content.setAlpha(0.5f);
} else { } else {
ViewHelper.setAlpha(holder.content, 1.0f); holder.content.setAlpha(1.0f);
} }
FeedMedia media = item.getMedia(); FeedMedia media = item.getMedia();
@ -174,7 +167,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.VISIBLE); holder.progress.setVisibility(View.VISIBLE);
} }
} else { } else {
holder.progress.setVisibility(View.GONE); holder.progress.setVisibility(View.INVISIBLE);
} }
if(media.isCurrentlyPlaying()) { if(media.isCurrentlyPlaying()) {
@ -183,7 +176,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.container.setBackgroundColor(normalBackGroundColor); holder.container.setBackgroundColor(normalBackGroundColor);
} }
} else { } else {
holder.progress.setVisibility(View.GONE); holder.progress.setVisibility(View.INVISIBLE);
holder.txtvDuration.setVisibility(View.GONE); holder.txtvDuration.setVisibility(View.GONE);
} }
@ -228,7 +221,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return pos; return pos;
} }
private View.OnClickListener secondaryActionListener = new View.OnClickListener() { private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag(); FeedItem item = (FeedItem) v.getTag();
@ -253,7 +246,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
ImageButton butSecondary; ImageButton butSecondary;
FeedItem item; FeedItem item;
WeakReference<MainActivity> mainActivityRef; WeakReference<MainActivity> mainActivityRef;
int position;
public Holder(View itemView) { public Holder(View itemView) {
super(itemView); super(itemView);
@ -266,18 +258,18 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get(); MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) { if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray(); long[] ids = itemAccess.getItemsIds().toArray();
mainActivity.loadChildFragment(ItemFragment.newInstance(ids, position)); mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
} }
} }
@Override @Override
public void onItemSelected() { public void onItemSelected() {
ViewHelper.setAlpha(itemView, 0.5f); itemView.setAlpha(0.5f);
} }
@Override @Override
public void onItemClear() { public void onItemClear() {
ViewHelper.setAlpha(itemView, 1.0f); itemView.setAlpha(1.0f);
} }
public FeedItem getFeedItem() { return item; } public FeedItem getFeedItem() { return item; }
@ -303,6 +295,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
} }
}; };
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null); FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
contextMenuInterface.setItemVisibility(R.id.mark_as_seen_item, item.isNew());
} }
} }

View File

@ -9,7 +9,6 @@ import android.text.Spannable;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.ClickableSpan; import android.text.style.ClickableSpan;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -20,9 +19,9 @@ import android.widget.TextView;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.ChapterUtils; import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
public class ChaptersListAdapter extends ArrayAdapter<Chapter> { public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
@ -144,9 +143,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
Chapter current = ChapterUtils.getCurrentChapter(media); Chapter current = ChapterUtils.getCurrentChapter(media);
if (current == sc) { if (current == sc) {
boolean darkTheme = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark; int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background);
int highlight = darkTheme ? R.color.highlight_dark : R.color.highlight_light;
int playingBackGroundColor = ContextCompat.getColor(getContext(), highlight);
holder.view.setBackgroundColor(playingBackGroundColor); holder.view.setBackgroundColor(playingBackGroundColor);
} else { } else {
holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent)); holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));

View File

@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter; package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;

View File

@ -6,6 +6,8 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
@ -80,13 +82,19 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show();
} }
} else { // media is downloaded } else { // media is downloaded
if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) { if (media.isCurrentlyPlaying()) {
context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); new PlaybackServiceStarter(context, media)
} .startWhenPrepared(true)
else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) { .shouldStream(false)
context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE)); .start();
} IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
else { } else if (media.isCurrentlyPaused()) {
new PlaybackServiceStarter(context, media)
.startWhenPrepared(true)
.shouldStream(false)
.start();
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE);
} else {
DBTasks.playMedia(context, media, false, true, false); DBTasks.playMedia(context, media, false, true, false);
} }
} }

View File

@ -19,7 +19,6 @@ import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
@ -29,11 +28,11 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
/** Displays a list of DownloadStatus entries. */ /** Displays a list of DownloadStatus entries. */
public class DownloadLogAdapter extends BaseAdapter { public class DownloadLogAdapter extends BaseAdapter {
private final String TAG = "DownloadLogAdapter"; private static final String TAG = "DownloadLogAdapter";
private Context context; private final Context context;
private ItemAccess itemAccess; private final ItemAccess itemAccess;
public DownloadLogAdapter(Context context, ItemAccess itemAccess) { public DownloadLogAdapter(Context context, ItemAccess itemAccess) {
super(); super();
@ -67,8 +66,6 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.type.setText(R.string.download_type_feed); holder.type.setText(R.string.download_type_feed);
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { } else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
holder.type.setText(R.string.download_type_media); holder.type.setText(R.string.download_type_media);
} else if (status.getFeedfileType() == FeedImage.FEEDFILETYPE_FEEDIMAGE) {
holder.type.setText(R.string.download_type_image);
} }
if (status.getTitle() != null) { if (status.getTitle() != null) {
holder.title.setText(status.getTitle()); holder.title.setText(status.getTitle());
@ -94,8 +91,7 @@ public class DownloadLogAdapter extends BaseAdapter {
} }
holder.reason.setText(reasonText); holder.reason.setText(reasonText);
holder.reason.setVisibility(View.VISIBLE); holder.reason.setVisibility(View.VISIBLE);
if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE && if(!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.retry.setVisibility(View.VISIBLE); holder.retry.setVisibility(View.VISIBLE);
holder.retry.setOnClickListener(clickListener); holder.retry.setOnClickListener(clickListener);
ButtonHolder btnHolder; ButtonHolder btnHolder;

View File

@ -12,11 +12,11 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.DateUtils;
@ -87,9 +87,9 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
.into(holder.imageView); .into(holder.imageView);
if(item.isPlayed()) { if(item.isPlayed()) {
ViewHelper.setAlpha(convertView, 0.5f); convertView.setAlpha(0.5f);
} else { } else {
ViewHelper.setAlpha(convertView, 1.0f); convertView.setAlpha(1.0f);
} }
holder.title.setText(item.getTitle()); holder.title.setText(item.getTitle());
@ -99,10 +99,12 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.pubDate.setText(pubDateStr); holder.pubDate.setText(pubDateStr);
FeedItem.State state = item.getState(); FeedItem.State state = item.getState();
if (state == FeedItem.State.PLAYING) { if (state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
holder.butSecondary.setEnabled(false); holder.butSecondary.setEnabled(false);
holder.butSecondary.setAlpha(0.5f);
} else { } else {
holder.butSecondary.setEnabled(true); holder.butSecondary.setEnabled(true);
holder.butSecondary.setAlpha(1.0f);
} }
holder.butSecondary.setFocusable(false); holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item); holder.butSecondary.setTag(item);
@ -111,7 +113,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
return convertView; return convertView;
} }
private View.OnClickListener secondaryActionListener = new View.OnClickListener() { private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag(); FeedItem item = (FeedItem) v.getTag();

View File

@ -19,11 +19,11 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
public class DownloadlistAdapter extends BaseAdapter { public class DownloadlistAdapter extends BaseAdapter {
public static final int SELECTION_NONE = -1; private static final int SELECTION_NONE = -1;
private int selectedItemIndex; private int selectedItemIndex;
private ItemAccess itemAccess; private final ItemAccess itemAccess;
private Context context; private final Context context;
public DownloadlistAdapter(Context context, public DownloadlistAdapter(Context context,
ItemAccess itemAccess) { ItemAccess itemAccess) {
@ -105,7 +105,7 @@ public class DownloadlistAdapter extends BaseAdapter {
return convertView; return convertView;
} }
private View.OnClickListener butSecondaryListener = new View.OnClickListener() { private final View.OnClickListener butSecondaryListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Downloader downloader = (Downloader) v.getTag(); Downloader downloader = (Downloader) v.getTag();
@ -121,15 +121,6 @@ public class DownloadlistAdapter extends BaseAdapter {
ImageButton butSecondary; ImageButton butSecondary;
} }
public int getSelectedItemIndex() {
return selectedItemIndex;
}
public void setSelectedItemIndex(int selectedItemIndex) {
this.selectedItemIndex = selectedItemIndex;
notifyDataSetChanged();
}
public interface ItemAccess { public interface ItemAccess {
int getCount(); int getCount();

View File

@ -17,13 +17,10 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType; 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.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
@ -34,16 +31,16 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
*/ */
public class FeedItemlistAdapter extends BaseAdapter { public class FeedItemlistAdapter extends BaseAdapter {
private ActionButtonCallback callback; private final ActionButtonCallback callback;
private final ItemAccess itemAccess; private final ItemAccess itemAccess;
private final Context context; private final Context context;
private boolean showFeedtitle; private final boolean showFeedtitle;
private int selectedItemIndex; private final int selectedItemIndex;
/** true if played items should be made partially transparent */ /** true if played items should be made partially transparent */
private boolean makePlayedItemsTransparent; private final boolean makePlayedItemsTransparent;
private final ActionButtonUtils actionButtonUtils; private final ActionButtonUtils actionButtonUtils;
public static final int SELECTION_NONE = -1; private static final int SELECTION_NONE = -1;
private final int playingBackGroundColor; private final int playingBackGroundColor;
private final int normalBackGroundColor; private final int normalBackGroundColor;
@ -62,11 +59,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.actionButtonUtils = new ActionButtonUtils(context); this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent; this.makePlayedItemsTransparent = makePlayedItemsTransparent;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_light);
}
normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent); normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent);
} }
@ -145,9 +138,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.statusUnread.setVisibility(View.INVISIBLE); holder.statusUnread.setVisibility(View.INVISIBLE);
} }
if(item.isPlayed() && makePlayedItemsTransparent) { if(item.isPlayed() && makePlayedItemsTransparent) {
ViewHelper.setAlpha(convertView, 0.5f); convertView.setAlpha(0.5f);
} else { } else {
ViewHelper.setAlpha(convertView, 1.0f); convertView.setAlpha(1.0f);
} }
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate()); String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
@ -157,7 +150,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedMedia media = item.getMedia(); FeedMedia media = item.getMedia();
if (media == null) { if (media == null) {
holder.episodeProgress.setVisibility(View.GONE); holder.episodeProgress.setVisibility(View.INVISIBLE);
holder.inPlaylist.setVisibility(View.INVISIBLE); holder.inPlaylist.setVisibility(View.INVISIBLE);
holder.type.setVisibility(View.INVISIBLE); holder.type.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE); holder.lenSize.setVisibility(View.INVISIBLE);
@ -176,7 +169,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else { } else {
if(media.getPosition() == 0) { if(media.getPosition() == 0) {
holder.episodeProgress.setVisibility(View.GONE); holder.episodeProgress.setVisibility(View.INVISIBLE);
} }
} }

View File

@ -48,10 +48,10 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
public class NavListAdapter extends BaseAdapter public class NavListAdapter extends BaseAdapter
implements SharedPreferences.OnSharedPreferenceChangeListener { implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final int VIEW_TYPE_COUNT = 3; private static final int VIEW_TYPE_COUNT = 3;
public static final int VIEW_TYPE_NAV = 0; public static final int VIEW_TYPE_NAV = 0;
public static final int VIEW_TYPE_SECTION_DIVIDER = 1; public static final int VIEW_TYPE_SECTION_DIVIDER = 1;
public static final int VIEW_TYPE_SUBSCRIPTION = 2; private static final int VIEW_TYPE_SUBSCRIPTION = 2;
/** /**
* a tag used as a placeholder to indicate if the subscription list should be displayed or not * a tag used as a placeholder to indicate if the subscription list should be displayed or not
@ -62,8 +62,8 @@ public class NavListAdapter extends BaseAdapter
private static List<String> tags; private static List<String> tags;
private static String[] titles; private static String[] titles;
private ItemAccess itemAccess; private final ItemAccess itemAccess;
private WeakReference<Activity> activity; private final WeakReference<Activity> activity;
private boolean showSubscriptionList = true; private boolean showSubscriptionList = true;
public NavListAdapter(ItemAccess itemAccess, Activity context) { public NavListAdapter(ItemAccess itemAccess, Activity context) {
@ -86,9 +86,7 @@ public class NavListAdapter extends BaseAdapter
private void loadItems() { private void loadItems() {
List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS)); List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS));
List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems(); List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems();
for(String hidden : hiddenFragments) { newTags.removeAll(hiddenFragments);
newTags.remove(hidden);
}
if (newTags.contains(SUBSCRIPTION_LIST_TAG)) { if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
// we never want SUBSCRIPTION_LIST_TAG to be in 'tags' // we never want SUBSCRIPTION_LIST_TAG to be in 'tags'

View File

@ -24,8 +24,8 @@ import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.Iconify;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.core.util.ThemeUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -51,7 +51,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private static final String TAG = QueueRecyclerAdapter.class.getSimpleName(); private static final String TAG = QueueRecyclerAdapter.class.getSimpleName();
private WeakReference<MainActivity> mainActivity; private final WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess; private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback; private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils; private final ActionButtonUtils actionButtonUtils;
@ -76,11 +76,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
this.itemTouchHelper = itemTouchHelper; this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked(); locked = UserPreferences.isQueueLocked();
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent); normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
} }
@ -198,12 +194,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
@Override @Override
public void onItemSelected() { public void onItemSelected() {
ViewHelper.setAlpha(itemView, 0.5f); itemView.setAlpha(0.5f);
} }
@Override @Override
public void onItemClear() { public void onItemClear() {
ViewHelper.setAlpha(itemView, 1.0f); itemView.setAlpha(1.0f);
} }
public void bind(FeedItem item) { public void bind(FeedItem item) {
@ -280,7 +276,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
progressLeft.setText(""); progressLeft.setText("");
} }
progressRight.setText(Converter.getDurationStringLong(media.getDuration())); progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.INVISIBLE);
} }
if(media.isCurrentlyPlaying()) { if(media.isCurrentlyPlaying()) {
@ -305,7 +301,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
} }
private View.OnClickListener secondaryActionListener = new View.OnClickListener() { private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag(); FeedItem item = (FeedItem) v.getTag();

View File

@ -11,7 +11,6 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
@ -97,7 +96,7 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle()); holder.subtitle.setText(result.getSubtitle());
} }
ViewHelper.setAlpha(convertView, item.isPlayed() ? 0.5f : 1.0f); convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f);
Glide.with(context) Glide.with(context)
.load(item.getFeed().getImageLocation()) .load(item.getFeed().getImageLocation())

View File

@ -23,8 +23,8 @@ import de.danoeh.antennapod.core.util.Converter;
* Adapter for the statistics list * Adapter for the statistics list
*/ */
public class StatisticsListAdapter extends BaseAdapter { public class StatisticsListAdapter extends BaseAdapter {
private Context context; private final Context context;
List<DBReader.StatisticsItem> feedTime = new ArrayList<>(); private List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
private boolean countAll = true; private boolean countAll = true;
public StatisticsListAdapter(Context context) { public StatisticsListAdapter(Context context) {

View File

@ -50,7 +50,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
} }
private int getAdjustedPosition(int origPosition) { private int getAdjustedPosition(int origPosition) {
assert(origPosition != getAddTilePosition());
return origPosition < getAddTilePosition() ? origPosition : origPosition - 1; return origPosition < getAddTilePosition() ? origPosition : origPosition - 1;
} }

View File

@ -124,7 +124,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @param json object holding the podcast information * @param json object holding the podcast information
* @throws JSONException * @throws JSONException
*/ */
public static Podcast fromSearch(JSONObject json) throws JSONException { public static Podcast fromSearch(JSONObject json) {
String title = json.optString("collectionName", ""); String title = json.optString("collectionName", "");
String imageUrl = json.optString("artworkUrl100", null); String imageUrl = json.optString("artworkUrl100", null);
String feedUrl = json.optString("feedUrl", null); String feedUrl = json.optString("feedUrl", null);
@ -162,7 +162,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
/** /**
* View holder object for the GridView * View holder object for the GridView
*/ */
class PodcastViewHolder { static class PodcastViewHolder {
/** /**
* ImageView holding the Podcast image * ImageView holding the Podcast image

View File

@ -23,15 +23,15 @@ public class ExportWorker {
private static final String TAG = "ExportWorker"; private static final String TAG = "ExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds"; private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
private ExportWriter exportWriter; private final ExportWriter exportWriter;
private File output; private final File output;
public ExportWorker(ExportWriter exportWriter) { public ExportWorker(ExportWriter exportWriter) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR), this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension())); DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
} }
public ExportWorker(ExportWriter exportWriter, @NonNull File output) { private ExportWorker(ExportWriter exportWriter, @NonNull File output) {
this.exportWriter = exportWriter; this.exportWriter = exportWriter;
this.output = output; this.output = output;
} }

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.asynctask; package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
@ -16,9 +15,9 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
/** Queues items for download in the background. */ /** Queues items for download in the background. */
public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> { public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
private Context context; private final Context context;
private ProgressDialog progDialog; private ProgressDialog progDialog;
private int[] selection; private final int[] selection;
public OpmlFeedQueuer(Context context, int[] selection) { public OpmlFeedQueuer(Context context, int[] selection) {
super(); super();
@ -56,13 +55,8 @@ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
return null; return null;
} }
@SuppressLint("NewApi")
public void executeAsync() { public void executeAsync() {
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { executeOnExecutor(THREAD_POOL_EXECUTOR);
executeOnExecutor(THREAD_POOL_EXECUTOR);
} else {
execute();
}
} }
} }

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.asynctask; package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
@ -21,12 +20,12 @@ public class OpmlImportWorker extends
AsyncTask<Void, Void, ArrayList<OpmlElement>> { AsyncTask<Void, Void, ArrayList<OpmlElement>> {
private static final String TAG = "OpmlImportWorker"; private static final String TAG = "OpmlImportWorker";
private Context context; private final Context context;
private Exception exception; private Exception exception;
private ProgressDialog progDialog; private ProgressDialog progDialog;
private Reader mReader; private final Reader mReader;
public OpmlImportWorker(Context context, Reader reader) { public OpmlImportWorker(Context context, Reader reader) {
super(); super();
@ -93,13 +92,8 @@ public class OpmlImportWorker extends
return exception != null; return exception != null;
} }
@SuppressLint("NewApi")
public void executeAsync() { public void executeAsync() {
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { executeOnExecutor(THREAD_POOL_EXECUTOR);
executeOnExecutor(THREAD_POOL_EXECUTOR);
} else {
execute();
}
} }
} }

View File

@ -6,7 +6,7 @@ import de.danoeh.antennapod.core.ClientConfig;
/** /**
* Configures the ClientConfig class of the core package. * Configures the ClientConfig class of the core package.
*/ */
public class ClientConfigurator { class ClientConfigurator {
private ClientConfigurator(){} private ClientConfigurator(){}

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.config;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity; import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.CastplayerActivity; import de.danoeh.antennapod.activity.CastplayerActivity;
@ -18,7 +19,11 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
return new Intent(context, CastplayerActivity.class); return new Intent(context, CastplayerActivity.class);
} }
if (mediaType == MediaType.VIDEO) { if (mediaType == MediaType.VIDEO) {
return new Intent(context, VideoplayerActivity.class); Intent i = new Intent(context, VideoplayerActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
}
return i;
} else { } else {
return new Intent(context, AudioplayerActivity.class); return new Intent(context, AudioplayerActivity.class);
} }

View File

@ -32,14 +32,14 @@ import de.danoeh.antennapod.core.util.LongList;
public class EpisodesApplyActionFragment extends Fragment { public class EpisodesApplyActionFragment extends Fragment {
public String TAG = "EpisodeActionFragment"; public static final String TAG = "EpisodeActionFragment";
public static final int ACTION_QUEUE = 1; public static final int ACTION_QUEUE = 1;
public static final int ACTION_MARK_PLAYED = 2; private static final int ACTION_MARK_PLAYED = 2;
public static final int ACTION_MARK_UNPLAYED = 4; private static final int ACTION_MARK_UNPLAYED = 4;
public static final int ACTION_DOWNLOAD = 8; private static final int ACTION_DOWNLOAD = 8;
public static final int ACTION_REMOVE = 16; public static final int ACTION_REMOVE = 16;
public static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED private static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
| ACTION_DOWNLOAD | ACTION_REMOVE; | ACTION_DOWNLOAD | ACTION_REMOVE;
private ListView mListView; private ListView mListView;

View File

@ -8,6 +8,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.gpoddernet.GpodnetService; import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.GpodnetPreferences;

View File

@ -43,7 +43,7 @@ public class ProxyDialog {
private static final String TAG = "ProxyDialog"; private static final String TAG = "ProxyDialog";
private Context context; private final Context context;
private MaterialDialog dialog; private MaterialDialog dialog;
@ -243,47 +243,44 @@ public class ProxyDialog {
txtvMessage.setTextColor(textColorPrimary); txtvMessage.setTextColor(textColorPrimary);
txtvMessage.setText("{fa-circle-o-notch spin} " + checking); txtvMessage.setText("{fa-circle-o-notch spin} " + checking);
txtvMessage.setVisibility(View.VISIBLE); txtvMessage.setVisibility(View.VISIBLE);
subscription = Observable.create(new Observable.OnSubscribe<Response>() { subscription = Observable.create((Observable.OnSubscribe<Response>) subscriber -> {
@Override String type = (String) spType.getSelectedItem();
public void call(Subscriber<? super Response> subscriber) { String host = etHost.getText().toString();
String type = (String) spType.getSelectedItem(); String port = etPort.getText().toString();
String host = etHost.getText().toString(); String username = etUsername.getText().toString();
String port = etPort.getText().toString(); String password = etPassword.getText().toString();
String username = etUsername.getText().toString(); int portValue = 8080;
String password = etPassword.getText().toString(); if(!TextUtils.isEmpty(port)) {
int portValue = 8080; portValue = Integer.valueOf(port);
if(!TextUtils.isEmpty(port)) { }
portValue = Integer.valueOf(port); SocketAddress address = InetSocketAddress.createUnresolved(host, portValue);
} Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase());
SocketAddress address = InetSocketAddress.createUnresolved(host, portValue); Proxy proxy = new Proxy(proxyType, address);
Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase()); OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder()
Proxy proxy = new Proxy(proxyType, address); .connectTimeout(10, TimeUnit.SECONDS)
OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder() .proxy(proxy);
.connectTimeout(10, TimeUnit.SECONDS) builder.interceptors().clear();
.proxy(proxy); OkHttpClient client = builder.build();
builder.interceptors().clear(); if(!TextUtils.isEmpty(username)) {
OkHttpClient client = builder.build(); String credentials = Credentials.basic(username, password);
if(!TextUtils.isEmpty(username)) { client.interceptors().add(chain -> {
String credentials = Credentials.basic(username, password); Request request = chain.request().newBuilder()
client.interceptors().add(chain -> { .header("Proxy-Authorization", credentials).build();
Request request = chain.request().newBuilder() return chain.proceed(request);
.header("Proxy-Authorization", credentials).build(); });
return chain.proceed(request); }
}); Request request = new Request.Builder()
} .url("http://www.google.com")
Request request = new Request.Builder() .head()
.url("http://www.google.com") .build();
.head() try {
.build(); Response response = client.newCall(request).execute();
try { subscriber.onNext(response);
Response response = client.newCall(request).execute(); } catch(IOException e) {
subscriber.onNext(response); subscriber.onError(e);
} catch(IOException e) { }
subscriber.onError(e); subscriber.onCompleted();
} })
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.newThread()) .subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(

View File

@ -56,7 +56,7 @@ public class RatingDialog {
} }
} }
public static void rateNow() { private static void rateNow() {
Context context = mContext.get(); Context context = mContext.get();
if(context == null) { if(context == null) {
return; return;
@ -69,11 +69,11 @@ public class RatingDialog {
saveRated(); saveRated();
} }
public static boolean rated() { private static boolean rated() {
return mPreferences.getBoolean(KEY_RATED, false); return mPreferences.getBoolean(KEY_RATED, false);
} }
public static void saveRated() { private static void saveRated() {
mPreferences mPreferences
.edit() .edit()
.putBoolean(KEY_RATED, true) .putBoolean(KEY_RATED, true)

View File

@ -1,7 +1,6 @@
package de.danoeh.antennapod.dialog; package de.danoeh.antennapod.dialog;
import android.content.Context; import android.content.Context;
import android.support.design.widget.Snackbar;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
@ -9,7 +8,6 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.Toast; import android.widget.Toast;
@ -26,7 +24,7 @@ public abstract class SleepTimerDialog {
private static final String TAG = SleepTimerDialog.class.getSimpleName(); private static final String TAG = SleepTimerDialog.class.getSimpleName();
private Context context; private final Context context;
private MaterialDialog dialog; private MaterialDialog dialog;
private EditText etxtTime; private EditText etxtTime;

View File

@ -52,7 +52,7 @@ public class VariableSpeedDialog {
builder.neutralText(R.string.close_label); builder.neutralText(R.string.close_label);
builder.onPositive((dialog, which) -> { builder.onPositive((dialog, which) -> {
if (Build.VERSION.SDK_INT >= 16) { // just to be safe if (Build.VERSION.SDK_INT >= 16) { // just to be safe
UserPreferences.enableSonic(true); UserPreferences.enableSonic();
if(showSpeedSelector) { if(showSpeedSelector) {
showSpeedSelectorDialog(context); showSpeedSelectorDialog(context);
} }

View File

@ -25,7 +25,7 @@ public class AddFeedFragment extends Fragment {
/** /**
* Preset value for url text field. * Preset value for url text field.
*/ */
public static final String ARG_FEED_URL = "feedurl"; private static final String ARG_FEED_URL = "feedurl";
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -66,4 +66,15 @@ public class AddFeedFragment extends Fragment {
return root; return root;
} }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
// So, we certainly *don't* have an options menu,
// but unless we say we do, old options menus sometimes
// persist. mfietz thinks this causes the ActionBar to be invalidated
setHasOptionsMenu(true);
}
} }

View File

@ -4,6 +4,8 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; 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.Fragment;
import android.support.v4.view.MenuItemCompat; import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
@ -36,12 +38,12 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
@ -69,24 +71,24 @@ public class AllEpisodesFragment extends Fragment {
private static final String PREF_SCROLL_POSITION = "scroll_position"; private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset"; private static final String PREF_SCROLL_OFFSET = "scroll_offset";
protected RecyclerView recyclerView; RecyclerView recyclerView;
protected AllEpisodesRecycleAdapter listAdapter; AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading; private ProgressBar progLoading;
protected List<FeedItem> episodes; List<FeedItem> episodes;
private List<Downloader> downloaderList; private List<Downloader> downloaderList;
private boolean itemsLoaded = false; private boolean itemsLoaded = false;
private boolean viewsCreated = false; private boolean viewsCreated = false;
private boolean isUpdatingFeeds; private boolean isUpdatingFeeds;
protected boolean isMenuInvalidationAllowed = false; boolean isMenuInvalidationAllowed = false;
protected Subscription subscription; Subscription subscription;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
protected boolean showOnlyNewEpisodes() { return false; } boolean showOnlyNewEpisodes() { return false; }
protected String getPrefName() { return DEFAULT_PREF_NAME; } String getPrefName() { return DEFAULT_PREF_NAME; }
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -165,7 +167,7 @@ public class AllEpisodesFragment extends Fragment {
} }
} }
protected void resetViewState() { void resetViewState() {
viewsCreated = false; viewsCreated = false;
listAdapter = null; listAdapter = null;
} }
@ -284,13 +286,17 @@ public class AllEpisodesFragment extends Fragment {
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
} }
try { // Mark as seen contains UI logic specific to All/New/FavoriteSegments,
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); // e.g., Undo with Snackbar,
} catch (DownloadRequestException e) { // and is handled by this class rather than the generic FeedItemMenuHandler
e.printStackTrace(); // Undo is useful for Mark as seen, given there is no UI to undo it otherwise,
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show(); // i.e., there is context menu item for Mark as new
if (R.id.mark_as_seen_item == item.getItemId()) {
markItemAsSeenWithUndo(selectedItem);
return true; return true;
} }
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
} }
@Override @Override
@ -299,10 +305,10 @@ public class AllEpisodesFragment extends Fragment {
R.layout.all_episodes_fragment); R.layout.all_episodes_fragment);
} }
protected View onCreateViewHelper(LayoutInflater inflater, View onCreateViewHelper(LayoutInflater inflater,
ViewGroup container, ViewGroup container,
Bundle savedInstanceState, Bundle savedInstanceState,
int fragmentResource) { int fragmentResource) {
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(fragmentResource, container, false); View root = inflater.inflate(fragmentResource, container, false);
@ -346,7 +352,7 @@ public class AllEpisodesFragment extends Fragment {
updateShowOnlyEpisodesListViewState(); updateShowOnlyEpisodesListViewState();
} }
protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() { private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override @Override
public int getCount() { public int getCount() {
@ -444,7 +450,7 @@ public class AllEpisodesFragment extends Fragment {
} }
} }
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) { if ((arg & EVENTS) != 0) {
@ -459,7 +465,7 @@ public class AllEpisodesFragment extends Fragment {
private void updateShowOnlyEpisodesListViewState() { private void updateShowOnlyEpisodesListViewState() {
} }
protected void loadItems() { void loadItems() {
if(subscription != null) { if(subscription != null) {
subscription.unsubscribe(); subscription.unsubscribe();
} }
@ -483,8 +489,40 @@ public class AllEpisodesFragment extends Fragment {
}, error -> Log.e(TAG, Log.getStackTraceString(error))); }, error -> Log.e(TAG, Log.getStackTraceString(error)));
} }
protected List<FeedItem> loadData() { List<FeedItem> loadData() {
return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT); return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
} }
void markItemAsSeenWithUndo(FeedItem item) {
if (item == null) {
return;
}
Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")");
if (subscription != null) {
subscription.unsubscribe();
}
// we're marking it as unplayed since the user didn't actually play it
// but they don't want it considered 'NEW' anymore
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
final Handler h = new Handler(getActivity().getMainLooper());
final Runnable r = () -> {
FeedMedia media = item.getMedia();
if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
}
};
Snackbar snackbar = Snackbar.make(getView(), getString(R.string.marked_as_seen_label),
Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
// don't forget to cancel the thing that's going to remove the media
h.removeCallbacks(r);
});
snackbar.show();
h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
}
} }

View File

@ -140,18 +140,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
if(items != null) { if(items != null) {
inflater.inflate(R.menu.downloads_completed, menu); inflater.inflate(R.menu.downloads_completed, menu);
MenuItem episodeActions = menu.findItem(R.id.episode_actions); menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
if(items.size() > 0) {
int[] attrs = {R.attr.action_bar_icon_color};
TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs);
int textColor = ta.getColor(0, Color.GRAY);
ta.recycle();
episodeActions.setIcon(new IconDrawable(getActivity(),
FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
episodeActions.setVisible(true);
} else {
episodeActions.setVisible(false);
}
} }
} }
@ -160,7 +149,7 @@ public class CompletedDownloadsFragment extends ListFragment {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.episode_actions: case R.id.episode_actions:
EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
.newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE); .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE);
((MainActivity) getActivity()).loadChildFragment(fragment); ((MainActivity) getActivity()).loadChildFragment(fragment);
return true; return true;
default: default:
@ -168,7 +157,7 @@ public class CompletedDownloadsFragment extends ListFragment {
} }
} }
private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() { private final DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@Override @Override
public int getCount() { public int getCount() {
return (items != null) ? items.size() : 0; return (items != null) ? items.size() : 0;
@ -189,7 +178,7 @@ public class CompletedDownloadsFragment extends ListFragment {
} }
}; };
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) { if ((arg & EVENTS) != 0) {

View File

@ -1,5 +1,7 @@
package de.danoeh.antennapod.fragment; package de.danoeh.antennapod.fragment;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.ListFragment; import android.support.v4.app.ListFragment;
@ -13,9 +15,11 @@ import android.widget.ListView;
import java.util.List; import java.util.List;
import android.widget.TextView;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter; import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DBWriter;
@ -82,7 +86,30 @@ public class DownloadLogFragment extends ListFragment {
getActivity().supportInvalidateOptionsMenu(); getActivity().supportInvalidateOptionsMenu();
} }
private DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() { @Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
DownloadStatus status = adapter.getItem(position);
String url = "unknown";
String message = getString(R.string.download_successful);
FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
if (media != null) {
url = media.getDownload_url();
}
if (!status.isSuccessful()) {
message = status.getReasonDetailed();
}
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(R.string.download_error_details);
builder.setMessage(getString(R.string.download_error_details_message, message, url));
builder.setPositiveButton(android.R.string.ok, null);
Dialog dialog = builder.show();
((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true);
}
private final DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() {
@Override @Override
public int getCount() { public int getCount() {
@ -99,7 +126,7 @@ public class DownloadLogFragment extends ListFragment {
} }
}; };
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override @Override
public void update(EventDistributor eventDistributor, Integer arg) { public void update(EventDistributor eventDistributor, Integer arg) {

View File

@ -25,7 +25,7 @@ public class DownloadsFragment extends Fragment {
public static final String ARG_SELECTED_TAB = "selected_tab"; public static final String ARG_SELECTED_TAB = "selected_tab";
public static final int POS_RUNNING = 0; public static final int POS_RUNNING = 0;
public static final int POS_COMPLETED = 1; private static final int POS_COMPLETED = 1;
public static final int POS_LOG = 2; public static final int POS_LOG = 2;
private static final String PREF_LAST_TAB_POSITION = "tab_position"; private static final String PREF_LAST_TAB_POSITION = "tab_position";
@ -78,9 +78,9 @@ public class DownloadsFragment extends Fragment {
viewPager.setCurrentItem(lastPosition); viewPager.setCurrentItem(lastPosition);
} }
public class DownloadsPagerAdapter extends FragmentPagerAdapter { public static class DownloadsPagerAdapter extends FragmentPagerAdapter {
Resources resources; final Resources resources;
public DownloadsPagerAdapter(FragmentManager fm, Resources resources) { public DownloadsPagerAdapter(FragmentManager fm, Resources resources) {
super(fm); super(fm);

View File

@ -21,10 +21,10 @@ public class EpisodesFragment extends Fragment {
public static final String TAG = "EpisodesFragment"; public static final String TAG = "EpisodesFragment";
private static final String PREF_LAST_TAB_POSITION = "tab_position"; private static final String PREF_LAST_TAB_POSITION = "tab_position";
public static final int POS_NEW_EPISODES = 0; private static final int POS_NEW_EPISODES = 0;
public static final int POS_ALL_EPISODES = 1; private static final int POS_ALL_EPISODES = 1;
public static final int POS_FAV_EPISODES = 2; private static final int POS_FAV_EPISODES = 2;
public static final int TOTAL_COUNT = 3; private static final int TOTAL_COUNT = 3;
private TabLayout tabLayout; private TabLayout tabLayout;
@ -79,7 +79,7 @@ public class EpisodesFragment extends Fragment {
public static class EpisodesPagerAdapter extends FragmentPagerAdapter { public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
private final Resources resources; private final Resources resources;
private AllEpisodesFragment[] fragments = { private final AllEpisodesFragment[] fragments = {
new NewEpisodesFragment(), new NewEpisodesFragment(),
new AllEpisodesFragment(), new AllEpisodesFragment(),
new FavoriteEpisodesFragment() new FavoriteEpisodesFragment()
@ -106,7 +106,7 @@ public class EpisodesFragment extends Fragment {
case POS_ALL_EPISODES: case POS_ALL_EPISODES:
return resources.getString(R.string.all_episodes_short_label); return resources.getString(R.string.all_episodes_short_label);
case POS_NEW_EPISODES: case POS_NEW_EPISODES:
return resources.getString(R.string.new_label); return resources.getString(R.string.new_episodes_label);
case POS_FAV_EPISODES: case POS_FAV_EPISODES:
return resources.getString(R.string.favorite_episodes_label); return resources.getString(R.string.favorite_episodes_label);
default: default:

View File

@ -1,6 +1,9 @@
package de.danoeh.antennapod.fragment; package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -14,11 +17,15 @@ import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.PlaybackController;
import rx.Single;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/** /**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity * Fragment which is supposed to be displayed outside of the MediaplayerActivity
@ -33,8 +40,8 @@ public class ExternalPlayerFragment extends Fragment {
private ImageButton butPlay; private ImageButton butPlay;
private TextView mFeedName; private TextView mFeedName;
private ProgressBar mProgressBar; private ProgressBar mProgressBar;
private PlaybackController controller; private PlaybackController controller;
private Subscription subscription;
public ExternalPlayerFragment() { public ExternalPlayerFragment() {
super(); super();
@ -56,8 +63,15 @@ public class ExternalPlayerFragment extends Fragment {
Log.d(TAG, "layoutInfo was clicked"); Log.d(TAG, "layoutInfo was clicked");
if (controller != null && controller.getMedia() != null) { if (controller != null && controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent( Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
getActivity(), controller.getMedia()));
if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) {
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
}
} }
}); });
return root; return root;
@ -68,10 +82,15 @@ public class ExternalPlayerFragment extends Fragment {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController(); controller = setupPlaybackController();
butPlay.setOnClickListener(v -> { butPlay.setOnClickListener(v -> {
if(controller != null) { if (controller != null) {
controller.playPause(); controller.playPause();
} }
}); });
loadMediaInfo();
}
public void connectToPlaybackService() {
controller.init();
} }
private PlaybackController setupPlaybackController() { private PlaybackController setupPlaybackController() {
@ -112,9 +131,9 @@ public class ExternalPlayerFragment extends Fragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
onPositionObserverUpdate();
controller.init(); controller.init();
mProgressBar.setProgress((int)
((double) controller.getPosition() / controller.getDuration() * 100));
} }
@Override @Override
@ -124,6 +143,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null) { if (controller != null) {
controller.release(); controller.release();
} }
if (subscription != null) {
subscription.unsubscribe();
}
} }
@Override @Override
@ -144,7 +166,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController(); controller = setupPlaybackController();
if (butPlay != null) { if (butPlay != null) {
butPlay.setOnClickListener(v -> { butPlay.setOnClickListener(v -> {
if(controller != null) { if (controller != null) {
controller.playPause(); controller.playPause();
} }
}); });
@ -154,50 +176,56 @@ public class ExternalPlayerFragment extends Fragment {
private boolean loadMediaInfo() { private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info"); Log.d(TAG, "Loading media info");
if (controller != null && controller.serviceAvailable()) { if (controller == null) {
Playable media = controller.getMedia(); Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
mFeedName.setText(media.getFeedTitle());
mProgressBar.setProgress((int)
((double) controller.getPosition() / controller.getDuration() * 100));
Glide.with(getActivity())
.load(media.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
if (controller.isPlayingVideoLocally()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
}
return true;
} else {
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
return false;
}
} else {
Log.w(TAG, "loadMediaInfo was called while playbackService was null!");
return false; return false;
} }
if (subscription != null) {
subscription.unsubscribe();
}
subscription = Single.create(subscriber -> subscriber.onSuccess(controller.getMedia()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> updateUi((Playable) media));
return true;
} }
private String getPositionString(int position, int duration) { private void updateUi(Playable media) {
return Converter.getDurationStringLong(position) + " / " if (media != null) {
+ Converter.getDurationStringLong(duration); txtvTitle.setText(media.getEpisodeTitle());
mFeedName.setText(media.getFeedTitle());
onPositionObserverUpdate();
Glide.with(getActivity())
.load(media.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
if (controller.isPlayingVideoLocally()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
}
} else {
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
}
} }
public PlaybackController getPlaybackControllerTestingOnly() { public PlaybackController getPlaybackControllerTestingOnly() {
return controller; return controller;
} }
public void onPositionObserverUpdate() { private void onPositionObserverUpdate() {
if (controller.getPosition() == PlaybackService.INVALID_TIME
|| controller.getDuration() == PlaybackService.INVALID_TIME) {
return;
}
mProgressBar.setProgress((int) mProgressBar.setProgress((int)
((double) controller.getPosition() / controller.getDuration() * 100)); ((double) controller.getPosition() / controller.getDuration() * 100));
} }

View File

@ -26,7 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
public class FavoriteEpisodesFragment extends AllEpisodesFragment { public class FavoriteEpisodesFragment extends AllEpisodesFragment {
public static final String TAG = "FavoriteEpisodesFrag"; private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment"; private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";

View File

@ -49,7 +49,7 @@ public class FyydSearchFragment extends Fragment {
private Button butRetry; private Button butRetry;
private TextView txtvEmpty; private TextView txtvEmpty;
private FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient()); private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
/** /**
* List of podcasts retreived from the search * List of podcasts retreived from the search
@ -169,7 +169,7 @@ public class FyydSearchFragment extends Fragment {
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
} }
void processSearchResult(FyydResponse response) { private void processSearchResult(FyydResponse response) {
adapter.clear(); adapter.clear();
if (!response.getData().isEmpty()) { if (!response.getData().isEmpty()) {
adapter.clear(); adapter.clear();

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