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.ar = core/src/main/res/values-ar/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_ES = core/src/main/res/values-ca-rES/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_KR = core/src/main/res/values-ko-rKR/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.no = core/src/main/res/values-no/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml

View File

@ -1,6 +1,32 @@
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
-------------

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"
alt="Get it on Google Play"
height="90">](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)
[<img src="https://f-droid.org/badge/get-it-on.png"
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="90">](https://f-droid.org/app/de.danoeh.antennapod)

View File

@ -1,7 +1,6 @@
import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: "com.android.application"
apply plugin: "me.tatarka.retrolambda"
apply plugin: 'com.github.triplet.play'
apply plugin: 'com.getkeepsafe.dexcount'
@ -15,7 +14,7 @@ buildscript {
jcenter()
}
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"
resValue "string", "provider_authority", "de.danoeh.antennapod.debug.provider"
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 {
resValue "string", "provider_authority", "de.danoeh.antennapod.provider"
minifyEnabled true
proguardFile "proguard.cfg"
signingConfig signingConfigs.releaseConfig
@ -113,10 +120,13 @@ android {
additionalParameters "--no-version-vectors"
}
flavorDimensions "market"
productFlavors {
free {
dimension "market"
}
play {
dimension "market"
}
}
@ -125,59 +135,55 @@ android {
}
}
configurations {
freeDebugCompile
freeReleaseCompile
playDebugCompile
playReleaseCompile
}
dependencies {
freeDebugCompile project(path: ":core", configuration: "freeDebug")
freeReleaseCompile project(path: ":core", configuration: "freeRelease")
freeImplementation project(":core")
// free build hack: skip some dependencies
if (!doFreeBuild()) {
playDebugCompile project(path: ":core", configuration: "playDebug")
playReleaseCompile project(path: ":core", configuration: "playRelease")
playImplementation project(":core")
} else {
System.out.println("app: free build hack, skipping some dependencies")
}
compile "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "com.android.support:gridlayout-v7:$supportVersion"
compile "com.android.support:percent:$supportVersion"
compile "com.android.support:recyclerview-v7:$supportVersion"
compile "org.apache.commons:commons-lang3:$commonslangVersion"
compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:appcompat-v7:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.android.support:gridlayout-v7:$supportVersion"
implementation "com.android.support:percent:$supportVersion"
implementation "com.android.support:recyclerview-v7:$supportVersion"
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"
}
compile "commons-io:commons-io:$commonsioVersion"
compile "org.jsoup:jsoup:$jsoupVersion"
compile "com.github.bumptech.glide:glide:$glideVersion"
compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
compile "com.squareup.okio:okio:$okioVersion"
compile "de.greenrobot:eventbus:$eventbusVersion"
compile "io.reactivex:rxandroid:$rxAndroidVersion"
compile "io.reactivex:rxjava:$rxJavaVersion"
implementation "commons-io:commons-io:$commonsioVersion"
implementation "org.jsoup:jsoup:$jsoupVersion"
implementation "com.github.bumptech.glide:glide:$glideVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
implementation "com.squareup.okio:okio:$okioVersion"
implementation "de.greenrobot:eventbus:$eventbusVersion"
implementation "io.reactivex:rxandroid:$rxAndroidVersion"
implementation "io.reactivex:rxjava:$rxJavaVersion"
// And ProGuard rules for RxJava!
compile "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
compile "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
compile "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
compile("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
implementation "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
implementation("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
transitive = true
}
compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
compile("com.githang:viewpagerindicator:2.5@aar") {
implementation "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
implementation("com.githang:viewpagerindicator:2.5.1@aar") {
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 {

View File

@ -125,3 +125,13 @@
-keep class com.squareup.moshi.** { *; }
-keep interface com.squareup.moshi.** { *; }
-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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.util.Collections.singletonList;
/**
* Test class for GpodnetService
@ -55,7 +57,7 @@ public class GPodnetServiceTest extends AndroidTestCase {
authenticate();
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> removed = Arrays.asList(URLS[0]);
List<String> removed = singletonList(URLS[0]);
List<String> added = Arrays.asList(URLS[2], URLS[3]);
service.uploadSubscriptions(USER, "radio", subscriptions);
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.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
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 {
private static final String FEEDS_DIR = "testfeeds";
File file = null;
OutputStream outputStream = null;
private File file = null;
private OutputStream outputStream = null;
protected void setUp() throws Exception {
super.setUp();
@ -82,15 +81,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(feed.getLink(), parsedFeed.getLink());
assertEquals(feed.getDescription(), parsedFeed.getDescription());
assertEquals(feed.getPaymentLink(), parsedFeed.getPaymentLink());
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());
}
assertEquals(feed.getImageUrl(), parsedFeed.getImageUrl());
if (feed.getItems() != null) {
assertNotNull(parsedFeed.getItems());
@ -119,14 +110,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(media.getMime_type(), parsedMedia.getMime_type());
}
if (item.hasItemImage()) {
assertTrue(parsedItem.hasItemImage());
FeedImage image = item.getImage();
FeedImage parsedImage = parsedItem.getImage();
assertEquals(image.getTitle(), parsedImage.getTitle());
assertEquals(image.getDownload_url(), parsedImage.getDownload_url());
}
assertEquals(item.getImageUrl(), parsedFeed.getImageUrl());
if (item.getChapters() != null) {
assertNotNull(parsedItem.getChapters());
@ -158,14 +142,10 @@ public class FeedHandlerTest extends InstrumentationTestCase {
}
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",
"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);
feed.setItems(new ArrayList<FeedItem>());
feed.setItems(new ArrayList<>());
for (int i = 0; i < numItems; 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.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.LocalPSMP;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
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.TimeUnit;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
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.storage.PodDBAdapter;
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 {
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;
protected Context context;
Context context;
protected File destFolder;
private File destFolder;
public DBCleanupTests() {
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,
List<File> files, int itemState, boolean addToQueue,
boolean addToFavorites) throws IOException {
void populateItems(final int numItems, Feed feed, List<FeedItem> items,
List<File> files, int itemState, boolean addToQueue,
boolean addToFavorites) throws IOException {
for (int i = 0; i < numItems; i++) {
Date itemDate = new Date(numItems - i);
Date playbackCompletionDate = null;

View File

@ -5,7 +5,6 @@ import android.test.FlakyTest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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.PodDBAdapter;
import static java.util.Collections.singletonList;
/**
* Test class for DBTasks
*/
@ -125,7 +127,7 @@ public class DBTasksTest extends InstrumentationTestCase {
public void testUpdateFeedMediaUrlResetState() {
final Feed feed = new Feed("url", null, "title");
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();
adapter.open();
@ -138,7 +140,7 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedMedia media = new FeedMedia(item, "url", 1024, "mime/type");
item.setMedia(media);
feed.setItems(Arrays.asList(item));
feed.setItems(singletonList(item));
final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(feed != newFeed);

View File

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

View File

@ -15,9 +15,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
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.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.SimpleChapter;
@ -124,89 +122,13 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
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);
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<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
@ -261,13 +183,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(null);
// 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);
feed.setImageUrl("url");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@ -275,21 +191,14 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
assertTrue(feed.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();
adapter.close();
}
@ -300,12 +209,7 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
feed.setImageUrl("url");
// create items
for (int i = 0; i < 10; i++) {
@ -320,24 +224,18 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.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);
@ -346,65 +244,6 @@ public class DBWriterTest extends InstrumentationTestCase {
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 {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@ -412,11 +251,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
feed.setImageUrl("url");
List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
@ -437,7 +272,6 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
@ -460,9 +294,6 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
@ -484,11 +315,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
image.setOwner(feed);
feed.setImage(image);
feed.setImageUrl("url");
List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
@ -509,7 +336,6 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
@ -522,9 +348,6 @@ public class DBWriterTest extends InstrumentationTestCase {
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);
@ -539,7 +362,7 @@ public class DBWriterTest extends InstrumentationTestCase {
private FeedMedia playbackHistorySetup(Date playbackCompletionDate) {
final Context context = getInstrumentation().getTargetContext();
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);
FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0);
feed.getItems().add(item);
@ -791,7 +614,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<FeedItem>());
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);

View File

@ -15,6 +15,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
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.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.preferences.PreferenceController;
/**
* User interface tests for MainActivity
@ -96,7 +96,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
// queue
openNavDrawer();
@ -155,11 +155,11 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testGoToPreferences() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.waitForActivity(PreferenceController.getPreferenceActivity());
solo.waitForActivity(PreferenceActivity.class);
}
public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
@ -190,7 +190,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
@ -209,7 +209,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
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> {
private static final String TAG = PlaybackTest.class.getSimpleName();
public static final int EPISODES_DRAWER_LIST_INDEX = 1;
public static final int QUEUE_DRAWER_LIST_INDEX = 0;
private static final int EPISODES_DRAWER_LIST_INDEX = 1;
private static final int QUEUE_DRAWER_LIST_INDEX = 0;
private Solo solo;
private UITestUtils uiTestUtils;

View File

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

View File

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

View File

@ -1,9 +1,7 @@
package de.test.antennapod.ui;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log;
import junit.framework.Assert;
@ -20,14 +18,12 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.R;
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.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
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.util.playback.PlaybackController;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
@ -39,27 +35,25 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class UITestUtils {
class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
private static final String DATA_FOLDER = "test/UITestUtils";
public static final int NUM_FEEDS = 5;
public static final int NUM_ITEMS_PER_FEED = 10;
private static final int NUM_FEEDS = 5;
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;
public static final String TEST_FILE_NAME = "3sec.mp3";
private static final String TEST_FILE_NAME = "3sec.mp3";
private Context context;
private HTTPBin server = new HTTPBin();
private final Context context;
private final HTTPBin server = new HTTPBin();
private File destDir;
private File hostedFeedDir;
private File hostedMediaDir;
public List<Feed> hostedFeeds = new ArrayList<>();
public final List<Feed> hostedFeeds = new ArrayList<>();
public UITestUtils(Context context) {
this.context = context;
@ -141,12 +135,9 @@ public class UITestUtils {
public void addHostedFeedData() throws IOException {
if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance");
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,
"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);
image.setOwner(feed);
// create items
List<FeedItem> items = new ArrayList<>();
@ -192,12 +183,6 @@ public class UITestUtils {
List<FeedItem> queue = new ArrayList<>();
for (Feed feed : hostedFeeds) {
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) {
for (FeedItem item : feed.getItems()) {
if (item.hasMedia()) {

View File

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

View File

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

View File

@ -1,8 +1,8 @@
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}.

View File

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

View File

@ -1,6 +1,7 @@
package de.test.antennapod.util;
import android.test.AndroidTestCase;
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_PLAIN = "text/plain";
private List<File> servedFiles;
private final List<File> servedFiles;
public HTTPBin() {
super(PORT);

View File

@ -88,15 +88,15 @@ public abstract class NanoHTTPD {
* This is required as the Keep-Alive HTTP connections would otherwise
* 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
*/
public static final String MIME_PLAINTEXT = "text/plain";
private static final String MIME_PLAINTEXT = "text/plain";
/**
* 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.
*/
@ -104,7 +104,7 @@ public abstract class NanoHTTPD {
private final String hostname;
private final int myPort;
private ServerSocket myServerSocket;
private Set<Socket> openConnections = new HashSet<>();
private final Set<Socket> openConnections = new HashSet<>();
private Thread myThread;
/**
* Pluggable strategy for asynchronously executing requests.
@ -118,14 +118,14 @@ public abstract class NanoHTTPD {
/**
* Constructs an HTTP server on given port.
*/
public NanoHTTPD(int port) {
NanoHTTPD(int port) {
this(null, 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.myPort = port;
setTempFileManagerFactory(new DefaultTempFileManagerFactory());
@ -168,44 +168,38 @@ public abstract class NanoHTTPD {
myServerSocket = new ServerSocket();
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
myThread = new Thread(new Runnable() {
@Override
public void run() {
do {
try {
final Socket finalAccept = myServerSocket.accept();
registerConnection(finalAccept);
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
final InputStream inputStream = finalAccept.getInputStream();
asyncRunner.exec(new Runnable() {
@Override
public void run() {
OutputStream outputStream = null;
try {
outputStream = finalAccept.getOutputStream();
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);
}
myThread = new Thread(() -> {
do {
try {
final Socket finalAccept = myServerSocket.accept();
registerConnection(finalAccept);
finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
final InputStream inputStream = finalAccept.getInputStream();
asyncRunner.exec(() -> {
OutputStream outputStream = null;
try {
outputStream = finalAccept.getOutputStream();
TempFileManager tempFileManager = tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
while (!finalAccept.isClosed()) {
session.execute();
}
});
} catch (IOException e) {
}
} while (!myServerSocket.isClosed());
}
} 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 (IOException e) {
}
} while (!myServerSocket.isClosed());
});
myThread.setDaemon(true);
myThread.setName("NanoHttpd Main Listener");
@ -232,7 +226,7 @@ public abstract class NanoHTTPD {
*
* @param socket the {@link Socket} for the connection.
*/
public synchronized void registerConnection(Socket socket) {
private synchronized void registerConnection(Socket socket) {
openConnections.add(socket);
}
@ -242,14 +236,14 @@ public abstract class NanoHTTPD {
* @param socket
* the {@link Socket} for the connection.
*/
public synchronized void unRegisterConnection(Socket socket) {
private synchronized void unRegisterConnection(Socket socket) {
openConnections.remove(socket);
}
/**
* Forcibly closes all connections that are open.
*/
public synchronized void closeAllConnections() {
private synchronized void closeAllConnections() {
for (Socket socket : openConnections) {
safeClose(socket);
}
@ -259,7 +253,7 @@ public abstract class NanoHTTPD {
return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
}
public final boolean wasStarted() {
private boolean wasStarted() {
return myServerSocket != null && myThread != null;
}
@ -294,7 +288,7 @@ public abstract class NanoHTTPD {
* @param session The HTTP session
* @return HTTP response, see class Response for details
*/
public Response serve(IHTTPSession session) {
Response serve(IHTTPSession session) {
Map<String, String> files = new ArrayMap<>();
Method method = session.getMethod();
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>
* @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;
try {
decoded = URLDecoder.decode(str, "UTF8");
@ -347,7 +341,7 @@ public abstract class NanoHTTPD {
* @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).
*/
protected Map<String, List<String>> decodeParameters(String queryString) {
private Map<String, List<String>> decodeParameters(String queryString) {
Map<String, List<String>> parms = new ArrayMap<>();
if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&");
@ -378,7 +372,7 @@ public abstract class NanoHTTPD {
*
* @param asyncRunner new strategy for handling threads.
*/
public void setAsyncRunner(AsyncRunner asyncRunner) {
private void setAsyncRunner(AsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner;
}
@ -393,7 +387,7 @@ public abstract class NanoHTTPD {
*
* @param tempFileManagerFactory new strategy for handling temp files.
*/
public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
private void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory;
}
@ -448,9 +442,9 @@ public abstract class NanoHTTPD {
* themselves up when no longer needed.</p>
*/
public interface TempFile {
OutputStream open() throws Exception;
OutputStream open();
void delete() throws Exception;
void delete();
String getName();
}
@ -528,12 +522,12 @@ public abstract class NanoHTTPD {
}
@Override
public OutputStream open() throws Exception {
public OutputStream open() {
return fstream;
}
@Override
public void delete() throws Exception {
public void delete() {
safeClose(fstream);
file.delete();
}
@ -563,7 +557,7 @@ public abstract class NanoHTTPD {
/**
* 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.
*/
@ -616,7 +610,7 @@ public abstract class NanoHTTPD {
/**
* Sends given response to the socket.
*/
protected void send(OutputStream outputStream) {
void send(OutputStream outputStream) {
String mime = mimeType;
SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
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")) {
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")) {
pw.print("Connection: keep-alive\r\n");
}
@ -694,7 +688,7 @@ public abstract class NanoHTTPD {
outputStream.write(buff, 0, read);
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 {
@ -844,7 +838,7 @@ public abstract class NanoHTTPD {
public static final int BUFSIZE = 8192;
private final TempFileManager tempFileManager;
private final OutputStream outputStream;
private PushbackInputStream inputStream;
private final PushbackInputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
@ -1144,17 +1138,19 @@ public abstract class NanoHTTPD {
String value = "";
if (item.get("content-type") == null) {
StringBuilder tmp = new StringBuilder();
while (mpline != null && !mpline.contains(boundary)) {
mpline = in.readLine();
if (mpline != null) {
int d = mpline.indexOf(boundary);
if (d == -1) {
value += mpline;
tmp.append(mpline);
} else {
value += mpline.substring(0, d - 2);
tmp.append(mpline.substring(0, d - 2));
}
}
}
value = tmp.toString();
} else {
if (boundarycount > bpositions.length) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
@ -1326,7 +1322,9 @@ public abstract class NanoHTTPD {
}
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) {
n = name;
@ -1366,8 +1364,8 @@ public abstract class NanoHTTPD {
* @author LordFokas
*/
public class CookieHandler implements Iterable<String> {
private ArrayMap<String, String> cookies = new ArrayMap<>();
private ArrayList<Cookie> queue = new ArrayList<>();
private final ArrayMap<String, String> cookies = new ArrayMap<>();
private final ArrayList<Cookie> queue = new ArrayList<>();
public CookieHandler(Map<String, String> httpHeaders) {
String raw = httpHeaders.get("cookie");

View File

@ -1,7 +1,7 @@
package de.test.antennapod.util.syndication;
import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@ -9,6 +9,8 @@ import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;
import de.danoeh.antennapod.core.util.syndication.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";
public static final long FEATURE_USE_RFC3339LOCAL = 1;
private static final long FEATURE_USE_RFC3339LOCAL = 1;
@Override
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;
import de.danoeh.antennapod.core.feed.Feed;
import java.io.IOException;
import java.io.OutputStream;
import de.danoeh.antennapod.core.feed.Feed;
/**
* 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 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
*/
public class GeneratorUtil {
class GeneratorUtil {
private GeneratorUtil(){}
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;
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 java.io.IOException;
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.
*/

View File

@ -2,6 +2,6 @@ package de.danoeh.antennapod.config;
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.
*/
public class PreferenceControllerFlavorHelper {
class PreferenceControllerFlavorHelper {
static void setupFlavoredUI(PreferenceController.PreferenceUI ui) {
ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setEnabled(false);

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
android:installLocation="auto"
android:versionCode="1060402"
android:versionName="1.6.4.2">
android:versionCode="1070000"
android:versionName="1.7.0">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
@ -33,11 +33,12 @@
<application
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:backupAgent=".core.backup.OpmlBackupAgent"
android:restoreAnyVersion="true"
android:logo="@drawable/ic_launcher">
android:logo="@mipmap/ic_launcher">
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" />
<meta-data
@ -96,15 +97,6 @@
android:name=".activity.DownloadAuthenticationActivity"
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
android:name=".activity.PreferenceActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -114,16 +106,25 @@
android:value="de.danoeh.antennapod.activity.MainActivity"/>
</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>
<service
android:name=".service.PlayerWidgetService"
android:name=".core.service.PlayerWidgetJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="false">
</service>
<receiver android:name=".receiver.PlayerWidget">
<receiver android:name=".core.receiver.PlayerWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
@ -176,6 +177,13 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</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
android:name=".activity.OpmlImportFromPathActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -224,7 +232,8 @@
<activity
android:name=".activity.VideoplayerActivity"
android:configChanges="keyboardHidden|orientation"
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
android:supportsPictureInPicture="true"
android:screenOrientation="sensorLandscape">
<meta-data
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()
.penaltyLog()
.penaltyDropBox();
if (Build.VERSION.SDK_INT >= 11) {
builder.detectActivityLeaks();
builder.detectLeakedClosableObjects();
}
builder.detectActivityLeaks();
builder.detectLeakedClosableObjects();
if(Build.VERSION.SDK_INT >= 16) {
builder.detectLeakedRegistrationObjects();
}

View File

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

View File

@ -6,41 +6,31 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import java.text.DecimalFormat;
import java.util.concurrent.atomic.AtomicBoolean;
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.util.playback.ExternalMedia;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/**
* Activity for playing audio files.
*/
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
protected void onResume() {
super.onResume();
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent();
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);
playExternalMedia(getIntent(), MediaType.AUDIO);
} else if (PlaybackService.isCasting()) {
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();
finish();
startActivity(intent);
@ -95,7 +85,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
UserPreferences.setPlaybackSpeed(String.valueOf(speed));
}
}
String speedStr = String.format("%.2fx", speed);
String speedStr = new DecimalFormat("0.00x").format(speed);
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.
*/
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
protected void onCreate(Bundle savedInstanceState) {

View File

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

View File

@ -2,51 +2,37 @@ 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 com.joanzapata.iconify.Iconify;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
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.core.util.LangUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
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.Subscription;
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";
private static final String TAG = "FeedInfoActivity";
private boolean autoDeleteChanged = false;
private Feed feed;
private ImageView imgvCover;
@ -70,15 +55,6 @@ public class FeedInfoActivity extends AppCompatActivity {
private TextView lblAuthor;
private TextView txtvAuthor;
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;
@ -88,56 +64,16 @@ public class FeedInfoActivity extends AppCompatActivity {
public void onClick(View v) {
if(feed != null && feed.getDownload_url() != null) {
String url = feed.getDownload_url();
if (android.os.Build.VERSION.SDK_INT >= 11) {
ClipData clipData = ClipData.newPlainText(url, url);
android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(clipData);
} else {
android.text.ClipboardManager cm = (android.text.ClipboardManager) FeedInfoActivity.this
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(url);
}
ClipData clipData = ClipData.newPlainText(url, url);
android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(clipData);
Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT);
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
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
@ -148,28 +84,20 @@ public class FeedInfoActivity extends AppCompatActivity {
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));
txtvDescription = (TextView) findViewById(R.id.txtvDescription);
lblLanguage = (TextView) findViewById(R.id.lblLanguage);
txtvLanguage = (TextView) findViewById(R.id.txtvLanguage);
lblAuthor = (TextView) findViewById(R.id.lblAuthor);
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
txtvAuthor = (TextView) findViewById(R.id.txtvDetailsAuthor);
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);
@ -185,7 +113,6 @@ public class FeedInfoActivity extends AppCompatActivity {
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
FeedPreferences prefs = feed.getPreferences();
Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
@ -194,6 +121,14 @@ public class FeedInfoActivity extends AppCompatActivity {
.fitCenter()
.dontAnimate()
.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());
@ -211,6 +146,7 @@ public class FeedInfoActivity extends AppCompatActivity {
if (!TextUtils.isEmpty(feed.getAuthor())) {
txtvAuthor.setText(feed.getAuthor());
txtvAuthorHeader.setText(feed.getAuthor());
} else {
lblAuthor.setVisibility(View.GONE);
txtvAuthor.setVisibility(View.GONE);
@ -224,113 +160,13 @@ public class FeedInfoActivity extends AppCompatActivity {
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
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();
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();
@ -375,34 +211,4 @@ public class FeedInfoActivity extends AppCompatActivity {
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.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@ -18,11 +18,10 @@ import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
import de.danoeh.antennapod.preferences.PreferenceController;
/** Guides the user through the authentication process */
public class FlattrAuthActivity extends ActionBarActivity {
public class FlattrAuthActivity extends AppCompatActivity {
private static final String TAG = "FlattrAuthActivity";
private TextView txtvExplanation;
@ -104,7 +103,7 @@ public class FlattrAuthActivity extends ActionBarActivity {
switch (item.getItemId()) {
case android.R.id.home:
if (authSuccessful) {
Intent intent = new Intent(this, PreferenceController.getPreferenceActivity());
Intent intent = new Intent(this, PreferenceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
} 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 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.Validate;
@ -64,7 +67,6 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
@ -83,7 +85,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String PREF_NAME = "MainActivityPrefs";
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_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_FEED_ID = "fragment_feed_id";
public static final String SAVE_BACKSTACK_COUNT = "backstackCount";
public static final String SAVE_TITLE = "title";
private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
private static final String SAVE_TITLE = "title";
public static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG,
@ -173,7 +175,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity()));
startActivity(new Intent(MainActivity.this, PreferenceActivity.class));
});
FragmentTransaction transaction = fm.beginTransaction();
@ -201,6 +203,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
transaction.commit();
checkFirstLaunch();
NotificationUtils.createChannels(this);
UserPreferences.restartUpdateAlarm(false);
}
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();
String[] navLabels = new String[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;
}
public void loadFragment(int index, Bundle args) {
private void loadFragment(int index, Bundle args) {
Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")");
if (index < navAdapter.getSubscriptionOffset()) {
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
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
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
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if(position < navAdapter.getTags().size()) {
@ -573,10 +577,29 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
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;
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;
case R.id.rename_item:
new RenameFeedDialog(this, feed).show();
@ -605,8 +628,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
remover.skipOnCompletion = true;
int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
sendBroadcast(new Intent(
PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
IntentUtils.sendLocalBroadcast(MainActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
}
}
remover.executeAsync();
@ -631,7 +653,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private DBReader.NavDrawerData navDrawerData;
private int selectedNavListIndex = 0;
private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
public int getCount() {
if (navDrawerData != null) {
@ -721,6 +743,15 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
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) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
@ -744,14 +775,12 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
View parentLayout = findViewById(R.id.drawer_layout);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if(event.action != null) {
snackbar.setAction(getString(R.string.undo), v -> {
event.action.run();
});
snackbar.setAction(getString(R.string.undo), v -> event.action.run());
}
snackbar.show();
}
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {

View File

@ -1,9 +1,11 @@
package de.danoeh.antennapod.activity;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
@ -11,6 +13,9 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
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.util.Log;
import android.view.Menu;
@ -33,21 +38,28 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.Locale;
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.FeedMedia;
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.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
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.StorageUtils;
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.Playable;
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.VariableSpeedDialog;
import rx.Observable;
@ -65,20 +77,21 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static final String TAG = "MediaplayerActivity";
private static final String PREFS = "MediaPlayerActivityPreferences";
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;
protected TextView txtvLength;
protected SeekBar sbPosition;
protected ImageButton butRev;
protected TextView txtvRev;
protected ImageButton butPlay;
protected ImageButton butFF;
protected TextView txtvFF;
protected ImageButton butSkip;
private TextView txtvPosition;
private TextView txtvLength;
SeekBar sbPosition;
private ImageButton butRev;
private TextView txtvRev;
private ImageButton butPlay;
private ImageButton butFF;
private TextView txtvFF;
private ImageButton butSkip;
protected boolean showTimeLeft = false;
private boolean showTimeLeft = 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;
}
protected static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
return activity.txtvRev;
}
protected void onSetSpeedAbilityChanged() {
private void onSetSpeedAbilityChanged() {
Log.d(TAG, "onSetSpeedAbilityChanged()");
updatePlaybackSpeedButton();
}
protected void onPlaybackSpeedChange() {
private void onPlaybackSpeedChange() {
updatePlaybackSpeedButtonText();
}
protected void onServiceQueried() {
private void onServiceQueried() {
supportInvalidateOptionsMenu();
}
protected void chooseTheme() {
void chooseTheme() {
setTheme(UserPreferences.getTheme());
}
protected void setScreenOn(boolean enable) {
void setScreenOn(boolean enable) {
}
@Override
@ -224,9 +237,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
protected void onPause() {
if(controller != null) {
controller.reinitServiceIfPaused();
controller.pause();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (controller != null) {
controller.reinitServiceIfPaused();
controller.pause();
}
}
super.onPause();
}
@ -248,7 +263,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
*/
protected abstract void onBufferEnd();
protected void onBufferUpdate(float progress) {
private void onBufferUpdate(float progress) {
if (sbPosition != null) {
sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
}
@ -257,7 +272,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
/**
* Current screen orientation.
*/
protected int orientation;
private int orientation;
@Override
protected void onStart() {
@ -266,6 +281,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
}
controller = newPlaybackController();
setupGUI();
loadMediaInfo();
onPositionObserverUpdate();
}
@Override
@ -316,11 +334,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
((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);
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_with_position_item).setVisible(isItemAndHasLink);
@ -368,7 +386,17 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| 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;
} else {
if (media != null) {
@ -546,7 +574,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
});
break;
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));
break;
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
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this);
if(controller != null) {
if (controller != null) {
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
* a video surface.
@ -609,7 +658,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract void clearStatusMsg();
protected void onPositionObserverUpdate() {
void onPositionObserverUpdate() {
if (controller == null || txtvPosition == null || txtvLength == null) {
return;
}
@ -645,12 +694,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* to the PlaybackService to ensure that the activity has the right
* FeedMedia object.
*/
protected boolean loadMediaInfo() {
boolean loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
if(controller == null || controller.getMedia() == null) {
return false;
}
Playable media = controller.getMedia();
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
onPositionObserverUpdate();
@ -659,18 +707,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true;
}
protected void updatePlaybackSpeedButton() {
void updatePlaybackSpeedButton() {
// Only meaningful on AudioplayerActivity, where it is overridden.
}
protected void updatePlaybackSpeedButtonText() {
void updatePlaybackSpeedButtonText() {
// Only meaningful on AudioplayerActivity, where it is overridden.
}
/**
* Abstract directions to skip forward or back (rewind) and encapsulates behavior to get or set preference (including update of UI on the skip buttons).
*/
static public enum SkipDirection {
public enum SkipDirection {
SKIP_FORWARD(
UserPreferences::getFastForwardSecs,
MediaplayerActivity::getTxtvFFFromActivity,
@ -753,7 +801,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
builder.create().show();
}
protected void setupGUI() {
void setupGUI() {
setContentView(getContentViewResourceId());
sbPosition = (SeekBar) findViewById(R.id.sbPosition);
txtvPosition = (TextView) findViewById(R.id.txtvPosition);
@ -823,11 +871,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
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) {
return;
}
@ -835,14 +884,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
}
protected void onPlayPause() {
void onPlayPause() {
if(controller == null) {
return;
}
controller.init();
controller.playPause();
}
protected void onFastForward() {
void onFastForward() {
if (controller == null) {
return;
}
@ -852,7 +902,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract int getContentViewResourceId();
void handleError(int errorCode) {
private void handleError(int errorCode) {
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
errorDialog.setTitle(R.string.error_label);
errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
@ -865,7 +915,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
errorDialog.create().show();
}
float prog;
private float prog;
@Override
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.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
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.DBTasks;
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.PlaybackController;
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.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
@ -72,16 +73,17 @@ import rx.schedulers.Schedulers;
*/
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_DESCR = 1;
private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3;
final String TAG = "MediaplayerInfoActivity";
private static final String PREFS = "AudioPlayerActivityPreferences";
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,
EpisodesFragment.TAG,
SubscriptionFragment.TAG,
@ -91,8 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
protected Button butPlaybackSpeed;
protected ImageButton butCastDisconnect;
Button butPlaybackSpeed;
ImageButton butCastDisconnect;
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
private ListView navList;
@ -112,6 +114,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
EventBus.getDefault().unregister(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportPostponeEnterTransition();
}
@Override
protected void onStop() {
super.onStop();
@ -145,7 +153,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
setTheme(UserPreferences.getNoTitleTheme());
}
protected void saveCurrentFragment() {
void saveCurrentFragment() {
if(pager == null) {
return;
}
@ -153,7 +161,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
prefs.edit()
.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem())
.commit();
.apply();
}
@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.setDrawerIndicatorEnabled(false);
drawerLayout.setDrawerListener(drawerToggle);
drawerLayout.addDrawerListener(drawerToggle);
navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter);
@ -263,7 +271,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceController.getPreferenceActivity()));
startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class));
});
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
@ -277,6 +285,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pageIndicator.setViewPager(pager);
loadLastFragment();
pager.onSaveInstanceState();
navList.post(this::supportStartPostponedEnterTransition);
}
@Override
@ -297,7 +307,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
return true;
}
public void notifyMediaPositionChanged() {
private void notifyMediaPositionChanged() {
if(pagerAdapter == null) {
return;
}
@ -387,12 +397,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
new RenameFeedDialog(this, feed).show();
return true;
case R.id.remove_item:
final FeedRemover remover = new FeedRemover(this, feed) {
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
};
final FeedRemover remover = new FeedRemover(this, feed);
ConfirmationDialog conDialog = new ConfirmationDialog(this,
R.string.remove_feed_label,
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@ -404,12 +409,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
Playable playable = controller.getMedia();
if (playable != null && playable instanceof FeedMedia) {
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");
remover.skipOnCompletion = true;
if(controller.getStatus() == PlayerStatus.PLAYING) {
sendBroadcast(new Intent(
PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
IntentUtils.sendLocalBroadcast(MediaplayerInfoActivity.this, 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();
String[] navLabels = new String[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);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if (event.action != null) {
snackbar.setAction(getString(R.string.undo), v -> {
event.action.run();
});
snackbar.setAction(getString(R.string.undo), v -> event.action.run());
}
snackbar.show();
}
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {

View File

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

View File

@ -1,14 +1,12 @@
package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.ActionBar;
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.MenuItem;
import android.view.ViewGroup;
@ -16,6 +14,9 @@ import android.widget.FrameLayout;
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.core.preferences.UserPreferences;
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
* 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 PreferenceController preferenceController;
private MainFragment prefFragment;
private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private PreferenceFragmentCompat fragment;
@Override
public Preference findPreference(CharSequence key) {
return prefFragment.findPreference(key);
public void setFragment(PreferenceFragmentCompat fragment) {
this.fragment = fragment;
}
@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;
}
};
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
// 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
preferenceController = new PreferenceController(preferenceUI);
prefFragment = new MainFragment();
getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
showPreferenceScreen(R.xml.preferences, false);
}
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
@ -88,24 +117,40 @@ public class PreferenceActivity extends AppCompatActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else {
getSupportFragmentManager().popBackStack();
}
return true;
default:
return false;
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class MainFragment extends PreferenceFragment {
@Override
public void onSearchResultClicked(SearchPreferenceResult result) {
showPreferenceScreen(result.getResourceFile(), true);
result.highlight(preferenceUI.getFragment());
}
public static class MainFragment extends PreferenceFragmentCompat {
private int screen;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onCreate();
if (activity != null && activity.preferenceController != null) {
activity.preferenceUI.setFragment(this);
activity.preferenceController.onCreate(screen);
}
}
@ -114,15 +159,17 @@ public class PreferenceActivity extends AppCompatActivity {
super.onResume();
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onResume();
activity.setTitle(PreferenceController.getTitleOfPage(screen));
activity.preferenceUI.setFragment(this);
activity.preferenceController.onResume(screen);
}
}
@Override
public void onPause() {
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onPause();
if (screen == R.xml.preferences_gpodder) {
activity.preferenceController.unregisterGpodnet();
}
super.onPause();
}
@ -130,8 +177,8 @@ public class PreferenceActivity extends AppCompatActivity {
@Override
public void onStop() {
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
activity.preferenceController.onStop();
if (screen == R.xml.preferences_storage) {
activity.preferenceController.unsubscribeExportSubscription();
}
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;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
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
* Date: 2016-12-03
* Project: AntennaPod
* Shows the AntennaPod logo while waiting for the main activity to start
*/
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
ProgressBar progressBar = findViewById(R.id.progressBar);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
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));
}
private BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {

View File

@ -7,29 +7,34 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.WindowCompat;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
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.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.
*/
@ -45,12 +50,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
private AtomicBoolean isSetup = new AtomicBoolean(false);
private final AtomicBoolean isSetup = new AtomicBoolean(false);
private LinearLayout controls;
private LinearLayout videoOverlay;
private AspectRatioVideoView videoview;
private ProgressBar progressIndicator;
private FrameLayout videoframe;
@Override
protected void chooseTheme() {
@ -70,20 +76,8 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
if (getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) {
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);
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
playExternalMedia(getIntent(), MediaType.VIDEO);
} else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
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
protected void onPause() {
videoControlsHider.stop();
if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
controller.pause();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
controller.pause();
}
}
super.onPause();
}
@ -126,7 +137,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void setupGUI() {
if(isSetup.getAndSet(true)) {
if (isSetup.getAndSet(true)) {
return;
}
super.setupGUI();
@ -134,34 +145,30 @@ public class VideoplayerActivity extends MediaplayerActivity {
controls = (LinearLayout) findViewById(R.id.controls);
videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
videoframe = (FrameLayout) findViewById(R.id.videoframe);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
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) {
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
if (Build.VERSION.SDK_INT >= 14) {
videoOverlay.setFitsSystemWindows(true);
}
videoOverlay.setFitsSystemWindows(true);
setupVideoControlsToggler();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() ->
videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight()));
}
@Override
protected void onAwaitingVideoSurface() {
setupVideoAspectRatio();
if (videoSurfaceCreated && controller != null) {
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());
}
}
@ -180,8 +187,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE);
}
View.OnTouchListener onVideoviewTouched = (v, event) -> {
private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
return true;
}
videoControlsHider.stop();
toggleVideoControlsVisibility();
if (videoControlsShowing) {
@ -194,11 +204,23 @@ public class VideoplayerActivity extends MediaplayerActivity {
};
@SuppressLint("NewApi")
void setupVideoControlsToggler() {
private void setupVideoControlsToggler() {
videoControlsHider.stop();
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() {
if (videoControlsShowing) {
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");
}
}
setupVideoAspectRatio();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Videosurface was destroyed");
videoSurfaceCreated = false;
if (controller != null && !destroyingDueToReload) {
if (controller != null && !destroyingDueToReload
&& UserPreferences.getVideoBackgroundBehavior()
!= UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
controller.notifyVideoSurfaceAbandoned();
}
}
@ -263,6 +287,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
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) {
Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
destroyingDueToReload = true;
@ -307,28 +338,31 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoOverlay.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")
private void hideVideoControls() {
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
if (animation != null) {
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);
private void hideVideoControls(boolean showAnimation) {
if (showAnimation) {
final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
if (animation != null) {
videoOverlay.startAnimation(animation);
controls.startAnimation(animation);
}
}
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);
controls.setVisibility(View.GONE);
}
private void hideVideoControls() {
hideVideoControls(true);
}
@Override
protected int getContentViewResourceId() {
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 final int DELAY = 2500;
private WeakReference<VideoplayerActivity> activity;
public VideoControlsHider(VideoplayerActivity activity) {
VideoControlsHider(VideoplayerActivity activity) {
this.activity = new WeakReference<>(activity);
}
private final Runnable hideVideoControls = () -> {
VideoplayerActivity vpa = activity.get();
if(vpa == null) {
VideoplayerActivity vpa = activity != null ? activity.get() : null;
if (vpa == null) {
return;
}
if (vpa.videoControlsShowing) {
Log.d(TAG, "Hiding video controls");
vpa.getSupportActionBar().hide();
ActionBar actionBar = vpa.getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
vpa.hideVideoControls();
vpa.videoControlsShowing = false;
}
@ -371,7 +434,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
this.postDelayed(hideVideoControls, DELAY);
}
public void stop() {
void stop() {
this.removeCallbacks(hideVideoControls);
}

View File

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

View File

@ -3,7 +3,7 @@ package de.danoeh.antennapod.adapter;
import de.danoeh.antennapod.core.feed.FeedItem;
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. */
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
* of a listitem.
*/
public class ActionButtonUtils {
class ActionButtonUtils {
private final int[] labels;
private final TypedArray drawables;

View File

@ -21,7 +21,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify;
import com.nineoldandroids.view.ViewHelper;
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.FeedMedia;
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.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@ -68,11 +67,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
this.actionButtonCallback = actionButtonCallback;
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
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.item = null;
holder.mainActivityRef = mainActivityRef;
holder.position = -1;
// so we can grab this later
view.setTag(holder);
@ -113,11 +107,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
final FeedItem item = itemAccess.getItem(position);
if (item == null) return;
holder.itemView.setOnLongClickListener(v -> {
this.position = position;
this.position = holder.getAdapterPosition();
return false;
});
holder.item = item;
holder.position = position;
holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
@ -129,9 +122,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.statusUnread.setVisibility(View.VISIBLE);
}
if(item.isPlayed()) {
ViewHelper.setAlpha(holder.content, 0.5f);
holder.content.setAlpha(0.5f);
} else {
ViewHelper.setAlpha(holder.content, 1.0f);
holder.content.setAlpha(1.0f);
}
FeedMedia media = item.getMedia();
@ -174,7 +167,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.VISIBLE);
}
} else {
holder.progress.setVisibility(View.GONE);
holder.progress.setVisibility(View.INVISIBLE);
}
if(media.isCurrentlyPlaying()) {
@ -183,7 +176,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.container.setBackgroundColor(normalBackGroundColor);
}
} else {
holder.progress.setVisibility(View.GONE);
holder.progress.setVisibility(View.INVISIBLE);
holder.txtvDuration.setVisibility(View.GONE);
}
@ -228,7 +221,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return pos;
}
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
@ -253,7 +246,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
ImageButton butSecondary;
FeedItem item;
WeakReference<MainActivity> mainActivityRef;
int position;
public Holder(View itemView) {
super(itemView);
@ -266,18 +258,18 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
mainActivity.loadChildFragment(ItemFragment.newInstance(ids, position));
mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
}
}
@Override
public void onItemSelected() {
ViewHelper.setAlpha(itemView, 0.5f);
itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
ViewHelper.setAlpha(itemView, 1.0f);
itemView.setAlpha(1.0f);
}
public FeedItem getFeedItem() { return item; }
@ -303,6 +295,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
}
};
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.style.ClickableSpan;
import android.text.util.Linkify;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@ -20,9 +19,9 @@ import android.widget.TextView;
import de.danoeh.antennapod.R;
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.Converter;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
@ -144,9 +143,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
Chapter current = ChapterUtils.getCurrentChapter(media);
if (current == sc) {
boolean darkTheme = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark;
int highlight = darkTheme ? R.color.highlight_dark : R.color.highlight_light;
int playingBackGroundColor = ContextCompat.getColor(getContext(), highlight);
int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background);
holder.view.setBackgroundColor(playingBackGroundColor);
} else {
holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));

View File

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

View File

@ -6,6 +6,8 @@ import android.widget.Toast;
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 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();
}
} else { // media is downloaded
if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) {
context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
}
else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) {
context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE));
}
else {
if (media.isCurrentlyPlaying()) {
new PlaybackServiceStarter(context, media)
.startWhenPrepared(true)
.shouldStream(false)
.start();
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
} 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);
}
}

View File

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

View File

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

View File

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

View File

@ -17,13 +17,10 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
@ -34,16 +31,16 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
*/
public class FeedItemlistAdapter extends BaseAdapter {
private ActionButtonCallback callback;
private final ActionButtonCallback callback;
private final ItemAccess itemAccess;
private final Context context;
private boolean showFeedtitle;
private int selectedItemIndex;
private final boolean showFeedtitle;
private final int selectedItemIndex;
/** true if played items should be made partially transparent */
private boolean makePlayedItemsTransparent;
private final boolean makePlayedItemsTransparent;
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 normalBackGroundColor;
@ -62,11 +59,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_light);
}
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent);
}
@ -145,9 +138,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.statusUnread.setVisibility(View.INVISIBLE);
}
if(item.isPlayed() && makePlayedItemsTransparent) {
ViewHelper.setAlpha(convertView, 0.5f);
convertView.setAlpha(0.5f);
} else {
ViewHelper.setAlpha(convertView, 1.0f);
convertView.setAlpha(1.0f);
}
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
@ -157,7 +150,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedMedia media = item.getMedia();
if (media == null) {
holder.episodeProgress.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.INVISIBLE);
holder.inPlaylist.setVisibility(View.INVISIBLE);
holder.type.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
@ -176,7 +169,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else {
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
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_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
@ -62,8 +62,8 @@ public class NavListAdapter extends BaseAdapter
private static List<String> tags;
private static String[] titles;
private ItemAccess itemAccess;
private WeakReference<Activity> activity;
private final ItemAccess itemAccess;
private final WeakReference<Activity> activity;
private boolean showSubscriptionList = true;
public NavListAdapter(ItemAccess itemAccess, Activity context) {
@ -86,9 +86,7 @@ public class NavListAdapter extends BaseAdapter
private void loadItems() {
List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS));
List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems();
for(String hidden : hiddenFragments) {
newTags.remove(hidden);
}
newTags.removeAll(hiddenFragments);
if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
// 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.joanzapata.iconify.Iconify;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.core.util.ThemeUtils;
import org.apache.commons.lang3.ArrayUtils;
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 WeakReference<MainActivity> mainActivity;
private final WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
@ -76,11 +76,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
@ -198,12 +194,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
@Override
public void onItemSelected() {
ViewHelper.setAlpha(itemView, 0.5f);
itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
ViewHelper.setAlpha(itemView, 1.0f);
itemView.setAlpha(1.0f);
}
public void bind(FeedItem item) {
@ -280,7 +276,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
progressLeft.setText("");
}
progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
progressBar.setVisibility(View.GONE);
progressBar.setVisibility(View.INVISIBLE);
}
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
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();

View File

@ -11,7 +11,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.nineoldandroids.view.ViewHelper;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
@ -97,7 +96,7 @@ public class SearchlistAdapter extends BaseAdapter {
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)
.load(item.getFeed().getImageLocation())

View File

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

View File

@ -50,7 +50,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
}
private int getAdjustedPosition(int origPosition) {
assert(origPosition != getAddTilePosition());
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
* @throws JSONException
*/
public static Podcast fromSearch(JSONObject json) throws JSONException {
public static Podcast fromSearch(JSONObject json) {
String title = json.optString("collectionName", "");
String imageUrl = json.optString("artworkUrl100", null);
String feedUrl = json.optString("feedUrl", null);
@ -162,7 +162,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
/**
* View holder object for the GridView
*/
class PodcastViewHolder {
static class PodcastViewHolder {
/**
* ImageView holding the Podcast image

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.config;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.CastplayerActivity;
@ -18,7 +19,11 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
return new Intent(context, CastplayerActivity.class);
}
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 {
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 String TAG = "EpisodeActionFragment";
public static final String TAG = "EpisodeActionFragment";
public static final int ACTION_QUEUE = 1;
public static final int ACTION_MARK_PLAYED = 2;
public static final int ACTION_MARK_UNPLAYED = 4;
public static final int ACTION_DOWNLOAD = 8;
private static final int ACTION_MARK_PLAYED = 2;
private static final int ACTION_MARK_UNPLAYED = 4;
private static final int ACTION_DOWNLOAD = 8;
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;
private ListView mListView;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ public class AddFeedFragment extends Fragment {
/**
* Preset value for url text field.
*/
public static final String ARG_FEED_URL = "feedurl";
private static final String ARG_FEED_URL = "feedurl";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -66,4 +66,15 @@ public class AddFeedFragment extends Fragment {
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.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
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.FeedItem;
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.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
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_OFFSET = "scroll_offset";
protected RecyclerView recyclerView;
protected AllEpisodesRecycleAdapter listAdapter;
RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
protected List<FeedItem> episodes;
List<FeedItem> episodes;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean isUpdatingFeeds;
protected boolean isMenuInvalidationAllowed = false;
boolean isMenuInvalidationAllowed = false;
protected Subscription subscription;
Subscription subscription;
private LinearLayoutManager layoutManager;
protected boolean showOnlyNewEpisodes() { return false; }
protected String getPrefName() { return DEFAULT_PREF_NAME; }
boolean showOnlyNewEpisodes() { return false; }
String getPrefName() { return DEFAULT_PREF_NAME; }
@Override
public void onCreate(Bundle savedInstanceState) {
@ -165,7 +167,7 @@ public class AllEpisodesFragment extends Fragment {
}
}
protected void resetViewState() {
void resetViewState() {
viewsCreated = false;
listAdapter = null;
}
@ -284,13 +286,17 @@ public class AllEpisodesFragment extends Fragment {
return super.onContextItemSelected(item);
}
try {
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
} catch (DownloadRequestException e) {
e.printStackTrace();
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
// Mark as seen contains UI logic specific to All/New/FavoriteSegments,
// e.g., Undo with Snackbar,
// and is handled by this class rather than the generic FeedItemMenuHandler
// Undo is useful for Mark as seen, given there is no UI to undo it otherwise,
// 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 FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
@Override
@ -299,10 +305,10 @@ public class AllEpisodesFragment extends Fragment {
R.layout.all_episodes_fragment);
}
protected View onCreateViewHelper(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState,
int fragmentResource) {
View onCreateViewHelper(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState,
int fragmentResource) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(fragmentResource, container, false);
@ -346,7 +352,7 @@ public class AllEpisodesFragment extends Fragment {
updateShowOnlyEpisodesListViewState();
}
protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
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
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
@ -459,7 +465,7 @@ public class AllEpisodesFragment extends Fragment {
private void updateShowOnlyEpisodesListViewState() {
}
protected void loadItems() {
void loadItems() {
if(subscription != null) {
subscription.unsubscribe();
}
@ -483,8 +489,40 @@ public class AllEpisodesFragment extends Fragment {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
protected List<FeedItem> loadData() {
List<FeedItem> loadData() {
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);
if(items != null) {
inflater.inflate(R.menu.downloads_completed, menu);
MenuItem episodeActions = menu.findItem(R.id.episode_actions);
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);
}
menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
}
}
@ -160,7 +149,7 @@ public class CompletedDownloadsFragment extends ListFragment {
switch (item.getItemId()) {
case R.id.episode_actions:
EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
.newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE);
.newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE);
((MainActivity) getActivity()).loadChildFragment(fragment);
return true;
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
public int getCount() {
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
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {

View File

@ -1,5 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@ -13,9 +15,11 @@ import android.widget.ListView;
import java.util.List;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
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.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@ -82,7 +86,30 @@ public class DownloadLogFragment extends ListFragment {
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
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
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 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;
private static final String PREF_LAST_TAB_POSITION = "tab_position";
@ -78,9 +78,9 @@ public class DownloadsFragment extends Fragment {
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) {
super(fm);

View File

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

View File

@ -1,6 +1,9 @@
package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@ -14,11 +17,15 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
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.playback.Playable;
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
@ -33,8 +40,8 @@ public class ExternalPlayerFragment extends Fragment {
private ImageButton butPlay;
private TextView mFeedName;
private ProgressBar mProgressBar;
private PlaybackController controller;
private Subscription subscription;
public ExternalPlayerFragment() {
super();
@ -56,8 +63,15 @@ public class ExternalPlayerFragment extends Fragment {
Log.d(TAG, "layoutInfo was clicked");
if (controller != null && controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent(
getActivity(), controller.getMedia()));
Intent intent = PlaybackService.getPlayerActivityIntent(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;
@ -68,10 +82,15 @@ public class ExternalPlayerFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController();
butPlay.setOnClickListener(v -> {
if(controller != null) {
if (controller != null) {
controller.playPause();
}
});
loadMediaInfo();
}
public void connectToPlaybackService() {
controller.init();
}
private PlaybackController setupPlaybackController() {
@ -112,9 +131,9 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
onPositionObserverUpdate();
controller.init();
mProgressBar.setProgress((int)
((double) controller.getPosition() / controller.getDuration() * 100));
}
@Override
@ -124,6 +143,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null) {
controller.release();
}
if (subscription != null) {
subscription.unsubscribe();
}
}
@Override
@ -144,7 +166,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(v -> {
if(controller != null) {
if (controller != null) {
controller.playPause();
}
});
@ -154,50 +176,56 @@ public class ExternalPlayerFragment extends Fragment {
private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info");
if (controller != null && controller.serviceAvailable()) {
Playable media = controller.getMedia();
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!");
if (controller == null) {
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
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) {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
private void updateUi(Playable media) {
if (media != null) {
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() {
return controller;
}
public void onPositionObserverUpdate() {
private void onPositionObserverUpdate() {
if (controller.getPosition() == PlaybackService.INVALID_TIME
|| controller.getDuration() == PlaybackService.INVALID_TIME) {
return;
}
mProgressBar.setProgress((int)
((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 static final String TAG = "FavoriteEpisodesFrag";
private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";

View File

@ -49,7 +49,7 @@ public class FyydSearchFragment extends Fragment {
private Button butRetry;
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
@ -169,7 +169,7 @@ public class FyydSearchFragment extends Fragment {
progressBar.setVisibility(View.VISIBLE);
}
void processSearchResult(FyydResponse response) {
private void processSearchResult(FyydResponse response) {
adapter.clear();
if (!response.getData().isEmpty()) {
adapter.clear();

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