Merge branch 'develop' into fix-2359

This commit is contained in:
Brad Pitcher 2019-04-10 08:21:55 -07:00 committed by GitHub
commit f2af5192e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
540 changed files with 9675 additions and 8342 deletions

View File

@ -21,7 +21,10 @@ jobs:
- v1-android-
- run:
command: ./gradlew assembleDebug :core:testPlayDebugUnitTest -PdisablePreDex
# To build release, we need to create a temporary keystore that can be used to sign the app
command: |
keytool -noprompt -genkey -v -keystore "app/keystore" -alias alias -storepass password -keypass password -keyalg RSA -validity 10 -dname "CN=antennapod.org, OU=dummy, O=dummy, L=dummy, S=dummy, C=US"
./gradlew assemblePlayRelease :core:testPlayReleaseUnitTest -PdisablePreDex
no_output_timeout: 1800
- store_artifacts:

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

@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
[antennapod.english]
[antennapod.core-values]
source_file = core/src/main/res/values/strings.xml
source_lang = en
trans.ast_ES = core/src/main/res/values-b+ast/strings.xml
@ -25,14 +25,14 @@ trans.he_IL = core/src/main/res/values-iw-rIL/strings.xml
trans.hi_IN = core/src/main/res/values-hi-rIN/strings.xml
trans.hu = core/src/main/res/values-hu/strings.xml
trans.id = core/src/main/res/values-id/strings.xml
trans.it = core/src/main/res/values-it/strings.xml
trans.it_IT = core/src/main/res/values-it-rIT/strings.xml
trans.it_IT = core/src/main/res/values-it/strings.xml
trans.is = core/src/main/res/values-is-rIS/strings.xml
trans.ja = core/src/main/res/values-ja/strings.xml
trans.kn_IN = core/src/main/res/values-kn-rIN/strings.xml
trans.ko = core/src/main/res/values-ko/strings.xml
trans.ko_KR = core/src/main/res/values-ko-rKR/strings.xml
trans.lt = core/src/main/res/values-lt/strings.xml
trans.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
@ -58,16 +58,75 @@ trans.zh_TW = core/src/main/res/values-zh-rTW/strings.xml
trans.zh_HK = core/src/main/res/values-zh-rHK/strings.xml
[antennapod.description]
file_filter = app/src/main/play/<lang>/listing/fulldescription
source_file = app/src/main/play/en-US/listing/fulldescription
source_lang = en
trans.ar = app/src/main/play/ar/listing/fulldescription
trans.az = app/src/main/play/az-AZ/listing/fulldescription
trans.bg = app/src/main/play/bg/listing/fulldescription
trans.ca = app/src/main/play/ca/listing/fulldescription
trans.cs = app/src/main/play/cs-CZ/listing/fulldescription
trans.da = app/src/main/play/da-DK/listing/fulldescription
trans.de = app/src/main/play/de-DE/listing/fulldescription
trans.el = app/src/main/play/el-GR/listing/fulldescription
trans.es = app/src/main/play/es-ES/listing/fulldescription
trans.et = app/src/main/play/et/listing/fulldescription
trans.fa = app/src/main/play/fa/listing/fulldescription
trans.fr = app/src/main/play/fr-FR/listing/fulldescription
trans.gl = app/src/main/play/gl-ES/listing/fulldescription
trans.hi_IN = app/src/main/play/hi-IN/listing/fulldescription
trans.hu = app/src/main/play/hu-HU/listing/fulldescription
trans.it_IT = app/src/main/play/it-IT/listing/fulldescription
trans.iw = app/src/main/play/iw-IL/listing/fulldescription
trans.ja = app/src/main/play/ja-JP/listing/fulldescription
trans.ko = app/src/main/play/ko-KR/listing/fulldescription
trans.lt = app/src/main/play/lt/listing/fulldescription
trans.nl = app/src/main/play/nl-NL/listing/fulldescription
trans.pl_PL = app/src/main/play/pl-PL/listing/fulldescription
trans.pt_BR = app/src/main/play/pt-BR/listing/fulldescription
trans.pt_PT = app/src/main/play/pt-PT/listing/fulldescription
trans.ro_RO = app/src/main/play/ro/listing/fulldescription
trans.ru_RU = app/src/main/play/ru-RU/listing/fulldescription
trans.sl_SI = app/src/main/play/sl/listing/fulldescription
trans.sv_SE = app/src/main/play/sv-SE/listing/fulldescription
trans.tr = app/src/main/play/tr-TR/listing/fulldescription
trans.uk_UA = app/src/main/play/uk/listing/fulldescription
trans.vi = app/src/main/play/vi/listing/fulldescription
trans.zh_CN = app/src/main/play/zh-CN/listing/fulldescription
trans.zh_TW = app/src/main/play/zh-TW/listing/fulldescription
[antennapod.shortdescription]
file_filter = app/src/main/play/<lang>/listing/shortdescription
source_file = app/src/main/play/en-US/listing/shortdescription
source_lang = en
[antennapod.changelog]
file_filter = app/src/main/play/<lang>/whatsnew
source_file = app/src/main/play/en-US/whatsnew
source_lang = en
trans.ar = app/src/main/play/ar/listing/shortdescription
trans.az = app/src/main/play/az-AZ/listing/shortdescription
trans.bg = app/src/main/play/bg/listing/shortdescription
trans.ca = app/src/main/play/ca/listing/shortdescription
trans.cs = app/src/main/play/cs-CZ/listing/shortdescription
trans.da = app/src/main/play/da-DK/listing/shortdescription
trans.de = app/src/main/play/de-DE/listing/shortdescription
trans.el = app/src/main/play/el-GR/listing/shortdescription
trans.es = app/src/main/play/es-ES/listing/shortdescription
trans.et = app/src/main/play/et/listing/shortdescription
trans.fa = app/src/main/play/fa/listing/shortdescription
trans.fr = app/src/main/play/fr-FR/listing/shortdescription
trans.gl = app/src/main/play/gl-ES/listing/shortdescription
trans.hi_IN = app/src/main/play/hi-IN/listing/shortdescription
trans.hu = app/src/main/play/hu-HU/listing/shortdescription
trans.it_IT = app/src/main/play/it-IT/listing/shortdescription
trans.iw = app/src/main/play/iw-IL/listing/shortdescription
trans.ja = app/src/main/play/ja-JP/listing/shortdescription
trans.ko = app/src/main/play/ko-KR/listing/shortdescription
trans.lt = app/src/main/play/lt/listing/shortdescription
trans.nl = app/src/main/play/nl-NL/listing/shortdescription
trans.pl_PL = app/src/main/play/pl-PL/listing/shortdescription
trans.pt_BR = app/src/main/play/pt-BR/listing/shortdescription
trans.pt_PT = app/src/main/play/pt-PT/listing/shortdescription
trans.ro_RO = app/src/main/play/ro/listing/shortdescription
trans.ru_RU = app/src/main/play/ru-RU/listing/shortdescription
trans.sl_SI = app/src/main/play/sl/listing/shortdescription
trans.sv_SE = app/src/main/play/sv-SE/listing/shortdescription
trans.tr = app/src/main/play/tr-TR/listing/shortdescription
trans.uk_UA = app/src/main/play/uk/listing/shortdescription
trans.vi = app/src/main/play/vi/listing/shortdescription
trans.zh_CN = app/src/main/play/zh-CN/listing/shortdescription
trans.zh_TW = app/src/main/play/zh-TW/listing/shortdescription

View File

@ -1,6 +1,25 @@
Change Log
==========
Version 1.7.1
-------------
* Fix for database corruption
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
-------------

View File

@ -1,107 +1,168 @@
DEVELOPERS
==========
Alan Orth
Alexander Terczka
alifeflow
amhokies
Anders Bo Rasmussen
Andrew Gaul
Andrey Krutov
Anthony Lieuallen
axq
brad
ByteHamster
Cameron Banga
Christian Ludwig
Christopher Szucko
Cj Malone
Colin Willson
Cédric Cabessa
Danial Klimkin
Daniel Oeh
David Carver
David Reiss
Dean Brettle
dethstar
Dirk Mueller
Domingos Lopes
drabux
egsavage
EirikV
Eoin Mcloughlin
eraymond
Ercan Erden
Falko Lehmann
Hannes Achleitner
hannesa2
Hanno Zulla
heckler01
Holger Jeromin
Humberto Fraga
InsidE
James Falcon
Jan Niehusmann
Jens Klingenberg
Jens Müller
Johan Liesén
Kaligule
Katrin Leinweber
keunes
Kevin Dalley
Koen Glotzbach
kroegerama
Kurian Vithayathil
LatinSuD
Lee Yeong Khang
lightonflux
ligi
Luis Cruz
Marc Lasson
Martin Fietz
Martin Olsson
mat tso
mateoeh
Matthew Gaffen
Matthias Schütz
Maurice Gilden
Meir Schwarz
Michael Kaiser
Michael Scarito
Mike Chelen
minusf
MolarAmbiguity
Mounir Lamouri
mr-intj
Nis Wechselberg
Oliver Crow
orelogo
Paul Ortyl
Raghul
Raghul Jagannathan
recalculated
Ross Harrison
Sam Lee
Sam Whited
saqura
Selivanov Pavel
Serge
Seth Golub
sevenmaster
Shantana Hardy
Simon Danner
Simon Rutishauser
Simon Schubert
Soso Tughushi
Spencer Visick
Stefan Mitrik
Terence Eden
Tim Butram
Tobias Preuss
Tom Hennen
Tom Tom
tommy watson
tuxayo
twiceyuan
Udi Finkelstein
VarunBarad
volhol
Volker Hollich
WangYun
William Seemann
ydinath
TRANSLATORS
===========
Arabic: abuzar3.khalid, iDemo
Azerbaijani: phoenixar
Catalan: dvd1985, javiercoll, xc70
Catalan (Spain): javiercoll
Chinese (China): bebeauties38, dudeG, Felix2yu, gaohongyuan, Guaidaodl, linxiangyu, molisiye, tupunco, wongsyrone, yangyang, YogaGuru
Arabic: abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
Asturian (Spain): enolp
Azerbaijani: danieloeh, kotfenix
Bulgarian: solusitor
Catalan: dvd1985, exort12, javiercoll, lambdani, marcmetallextrem, xc70
Catalan (Spain): 00c0c0, javiercoll
Chinese: dillonbecker, RainSlide, xukeek, yangyang
Chinese (China): bebeauties38, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
Chinese (Taiwan): gugod, nigelinux, pggdt, ymhuang0808
Czech (Czech Republic): elich, Hanzmeister, mcepl, petnek, svetlemodry
Danish: CasperHN
Dutch: e2jk, glotzbach, rwv
Danish: CasperHN, jhertel
Dutch: e2jk, glotzbach, rwv, Vistaus
English: mfietz, sterylmreep
Estonian: Eraser
Finnish: danieloeh
French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, repat, sterylmreep, vcariven
German: 112358, altegedanken, bitsunited, ChaoticMind, Chaquotay, dab0015, DJaeger, HolgerJeromin, kalei, lohmann, mfietz, nilso, picsel2, repat, SAPlayer, schafia, ypid
Greek: AlexanderKanetakis
Hebrew (Israel): amir.dafnyman
Hindi (India): purple.coder, siddhusengar
Hungarian: glatz.balazs, naren93
Indonesian: luke137, silvanael16
Italian: aalex70, apanontin, Guybrush88, theloca95
Italian (Italy): aalex70, apanontin, Guybrush88, m.chinni, theloca95
French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, Poussinou, PRIMOKORN, repat, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
Galician: antiparvos, pikamoku, Raichely
German: 112358, altegedanken, barilla, bitsunited, Buggi, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
Greek: antonist, danieloeh, hua2016s, MSavoritias, pavlosv
Hebrew (Israel): amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
Hindi (India): nmabhinandan, purple.coder, siddhusengar
Hungarian: glatz.balazs, lna91, naren93, tszauer, ttyborg42
Icelandic: marthjod
Indonesian: jff, luke137, rezafaiza, silvanael16
Italian: aalex70, allin, apanontin, Bonnee, giuseppep, Guybrush88, marco_pag, neonsoftware, sevenmaster, theloca95
Italian (Italy): aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, m.chinni, neonsoftware, nixxo, sevenmaster, theloca95
Japanese: Naofumi, RACER1, sh3llc4t, TranslatorG
Kannada (India): thejeshgn
Korean: changwoo, halcyonest, seungrye, skcha
Korean: changwoo, seungrye, skcha
Korean (South Korea): changwoo, seungrye
Lithuanian: naglis
Macedonian: krisfremen
Norwegian: hakonanes, timbast
Norwegian Bokmål: hakonanes
Norwegian Bokmål (Norway): hakonanes, kongk, swordfighter, timbast
Polish: Iwangelion, maniexx, thedead4fun
Polish (Poland): Iwangelion, lomapur, maniexx, Mephistofeles, shark103, tyle
Portuguese: emansije, smarquespt
Portuguese (Brazil): alexupits, edman, Firmino, lipefire, lucasmotacr, mbaltar, rogervezaro, SamWilliam, silvanael16
Romanian (Romania): corneliu.e, fuzzmz
Russian (Russia): astra1, Duke_Raven, mercutiy, null, overmind88, phoenixar, s.chebotar, skvheadless, whereisthetea, zhenya97
Spanish: coperfix, dvd1985, Fitoschido, frandavid100, javiercoll, LatinSuD, tres.14159
Spanish (Spain): dvd1985, e2jk, frandavid100
Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, Lumen, nilso, SharpMelon, TwoD
Turkish: basarancaner, brsata, overbite
Ukrainian (Ukraine): older, zhenya97
Vietnamese: ppanhh, vietnamesel10n
Norwegian Bokmål: corkie, hakonanes
Norwegian Bokmål (Norway): corkie, hakonanes, kongk, timbast
Persian: ahangarha, F7D
Polish: Iwangelion, maniexx, mfloryan, thedead4fun
Polish (Poland): d6210809, Iwangelion, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
Portuguese: domingos86, emansije, smarquespt
Portuguese (Brazil): alexupits, alysonborges, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
Romanian (Romania): corneliu.e, fuzzmz, ralienpp
Russian (Russia): astra1, btimofeev, Duke_Raven, GaynullinDima, MegMasters98, mercutiy, null, overmind88, s.chebotar, shams4real, skvheadless, un_logic, whereisthetea, zhenya97
Slovenian (Slovenia): panter23
Spanish: AleksSyntek, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
Spanish (Spain): dvd1985, e2jk, frandavid100, hard_ware, palopezv, Raichely, TacoTheDank
Swahili (Kenya): BonfaceKilz
Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TwoD
Telugu: veeven
Turkish: basarancaner, brsata, Erdy, golcuk, overbite
Ukrainian (Ukraine): older, sergiyr, zhenya97
Vietnamese: abnvolk, nguyenvui, ppanhh, vietnamesel10n
Vietnamese (Vietnam): bizover

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,49 +1,27 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id('com.github.triplet.play') version '2.0.0'
}
apply plugin: "com.android.application"
apply plugin: 'com.github.triplet.play'
apply plugin: 'com.getkeepsafe.dexcount'
repositories {
maven { url "https://jitpack.io" }
mavenCentral()
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
}
}
def getMyVersionName() {
def parsedManifestXml = (new XmlSlurper())
.parse("${projectDir}/src/main/AndroidManifest.xml")
.declareNamespace(android: "http://schemas.android.com/apk/res/android")
return parsedManifestXml."@android:versionName"
}
def getMyVersionCode() {
def parsedManifestXml = (new XmlSlurper())
.parse("${projectDir}/src/main/AndroidManifest.xml")
.declareNamespace(android: "http://schemas.android.com/apk/res/android")
return parsedManifestXml."@android:versionCode".toInteger()
}
import org.apache.tools.ant.filters.ReplaceTokens
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode getMyVersionCode()
versionName "${getMyVersionName()}"
vectorDrawables.useSupportLibrary true
// Version code schema:
// "1.2.3-SNAPSHOT" -> 1020300
// "1.2.3-RC4" -> 1020304
versionCode 1070196
versionName "1.7.1"
testApplicationId "de.test.antennapod"
testInstrumentationRunner "de.test.antennapod.AntennaPodTestRunner"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
generatedDensities = []
}
@ -95,7 +73,7 @@ android {
release {
resValue "string", "provider_authority", "de.danoeh.antennapod.provider"
minifyEnabled true
proguardFile "proguard.cfg"
proguardFiles getDefaultProguardFile('proguard-android.txt'), "proguard.cfg"
signingConfig signingConfigs.releaseConfig
buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey
buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
@ -120,6 +98,10 @@ android {
additionalParameters "--no-version-vectors"
}
testOptions {
animationsDisabled = true
}
flavorDimensions "market"
productFlavors {
free {
@ -150,6 +132,7 @@ dependencies {
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"
@ -157,14 +140,14 @@ dependencies {
implementation "commons-io:commons-io:$commonsioVersion"
implementation "org.jsoup:jsoup:$jsoupVersion"
implementation "com.github.bumptech.glide:glide:$glideVersion"
annotationProcessor "com.github.bumptech.glide:compiler:$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!
implementation "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
implementation("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
@ -176,18 +159,33 @@ dependencies {
}
implementation "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
implementation 'com.leinardi.android:speed-dial:1.0.2' // 1.0.2 uses support 27.1.1 ; newer versions use 28.0.0;
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
implementation 'com.github.mfietz:fyydlin:v0.3'
implementation 'com.github.ByteHamster:SearchPreference:v1.0.8'
implementation 'com.github.mfietz:fyydlin:v0.4.2'
implementation 'com.github.ByteHamster:SearchPreference:v1.2.5'
implementation "org.awaitility:awaitility:$awaitilityVersion"
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
}
play {
serviceAccountEmail = '522080222319-compute@developer.gserviceaccount.com'
pk12File = file('../serviceaccount-c3d7d0f61387.p12')
if (project.hasProperty("antennaPodServiceAccountEmail")) {
serviceAccountEmail = antennaPodServiceAccountEmail
} else {
serviceAccountEmail = '522080222319-compute@developer.gserviceaccount.com'
}
if (project.hasProperty("antennaPodPk12File")) {
serviceAccountCredentials = file(antennaPodPk12File)
} else {
serviceAccountCredentials = file('../serviceaccount-c3d7d0f61387.p12')
}
}
// about.html is templatized so that we can automatically insert
@ -217,10 +215,3 @@ task copyTextFiles(type: Copy) {
preBuild.dependsOn filterAbout, copyTextFiles
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint" << "-Xlint:-deprecation" << "-Xlint:-serial"
}
}
}

View File

@ -53,8 +53,12 @@
public *;
}
# for okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-dontwarn org.codehaus.mojo.animal_sniffer.*
# for RxJava:
-dontwarn sun.misc.Unsafe
@ -71,8 +75,6 @@
-dontwarn android.support.v7.**
-dontwarn com.google.android.wearable.**
-keepattributes *Annotation*
-keep class org.shredzone.flattr4j.** { *; }
-dontwarn org.shredzone.flattr4j.**
@ -94,16 +96,19 @@
-keepclassmembers class ** {
public void onEvent*(**);
}
-keep class de.danoeh.antennapod.core.event.*
# android-iconify
-keep class com.joanzapata.** { *; }
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder
# for ViewPageIndicator problems (https://github.com/JakeWharton/ViewPagerIndicator/issues/366):
-dontwarn com.viewpagerindicator.LinePageIndicator
@ -125,3 +130,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

@ -1,18 +0,0 @@
package de.test.antennapod;
import android.test.InstrumentationTestRunner;
import android.test.suitebuilder.TestSuiteBuilder;
import junit.framework.TestSuite;
public class AntennaPodTestRunner extends InstrumentationTestRunner {
@Override
public TestSuite getAllTests() {
return new TestSuiteBuilder(AntennaPodTestRunner.class)
.includeAllPackagesUnderHere()
.excludePackages("de.test.antennapod.gpodnet")
.build();
}
}

View File

@ -0,0 +1,38 @@
package de.test.antennapod;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import java.util.concurrent.atomic.AtomicInteger;
public class NthMatcher {
public static <T> Matcher<T> first(final Matcher<T> matcher) {
return nth(matcher, 1);
}
public static <T> Matcher<T> second(final Matcher<T> matcher) {
return nth(matcher, 2);
}
private static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
return new BaseMatcher<T>() {
AtomicInteger count = new AtomicInteger(0);
@Override
public boolean matches(final Object item) {
if (matcher.matches(item)) {
if (count.incrementAndGet() == index) {
return true;
}
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("should return first matching item");
}
};
}
}

View File

@ -1,6 +1,7 @@
package de.test.antennapod.feed;
import android.test.AndroidTestCase;
import de.danoeh.antennapod.core.feed.FeedItem;
public class FeedItemTest extends AndroidTestCase {

View File

@ -1,43 +1,43 @@
package de.test.antennapod.gpodnet;
import android.test.AndroidTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.support.test.runner.AndroidJUnit4;
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 org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import static java.util.Collections.singletonList;
/**
* Test class for GpodnetService
*/
public class GPodnetServiceTest extends AndroidTestCase {
@Ignore
@RunWith(AndroidJUnit4.class)
public class GPodnetServiceTest {
private GpodnetService service;
private static final String USER = "";
private static final String PW = "";
@Override
protected void setUp() throws Exception {
super.setUp();
@Before
protected void setUp() {
service = new GpodnetService();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
private void authenticate() throws GpodnetServiceException {
service.authenticate(USER, PW);
}
@Test
public void testUploadSubscription() throws GpodnetServiceException {
authenticate();
ArrayList<String> l = new ArrayList<>();
@ -45,6 +45,7 @@ public class GPodnetServiceTest extends AndroidTestCase {
service.uploadSubscriptions(USER, "radio", l);
}
@Test
public void testUploadSubscription2() throws GpodnetServiceException {
authenticate();
ArrayList<String> l = new ArrayList<>();
@ -53,6 +54,7 @@ public class GPodnetServiceTest extends AndroidTestCase {
service.uploadSubscriptions(USER, "radio", l);
}
@Test
public void testUploadChanges() throws GpodnetServiceException {
authenticate();
String[] URLS = {"http://bitsundso.de/feed", "http://gamesundso.de/feed", "http://cre.fm/feed/mp3/", "http://freakshow.fm/feed/m4a/"};
@ -63,53 +65,63 @@ public class GPodnetServiceTest extends AndroidTestCase {
service.uploadChanges(USER, "radio", added, removed);
}
@Test
public void testGetSubscriptionChanges() throws GpodnetServiceException {
authenticate();
service.getSubscriptionChanges(USER, "radio", 1362322610L);
}
@Test
public void testGetSubscriptionsOfUser()
throws GpodnetServiceException {
authenticate();
service.getSubscriptionsOfUser(USER);
}
@Test
public void testGetSubscriptionsOfDevice()
throws GpodnetServiceException {
authenticate();
service.getSubscriptionsOfDevice(USER, "radio");
}
@Test
public void testConfigureDevices() throws GpodnetServiceException {
authenticate();
service.configureDevice(USER, "foo", "This is an updated caption",
GpodnetDevice.DeviceType.LAPTOP);
}
@Test
public void testGetDevices() throws GpodnetServiceException {
authenticate();
service.getDevices(USER);
}
@Test
public void testGetSuggestions() throws GpodnetServiceException {
authenticate();
service.getSuggestions(10);
}
@Test
public void testTags() throws GpodnetServiceException {
service.getTopTags(20);
}
@Test
public void testPodcastForTags() throws GpodnetServiceException {
List<GpodnetTag> tags = service.getTopTags(20);
service.getPodcastsForTag(tags.get(1),
10);
}
@Test
public void testSearch() throws GpodnetServiceException {
service.searchPodcasts("linux", 64);
}
@Test
public void testToplist() throws GpodnetServiceException {
service.getPodcastToplist(10);
}

View File

@ -1,6 +1,7 @@
package de.test.antennapod.handler;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.test.InstrumentationTestCase;
import org.xml.sax.SAXException;
@ -17,7 +18,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;
@ -37,7 +37,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
protected void setUp() throws Exception {
super.setUp();
Context context = getInstrumentation().getContext();
Context context = InstrumentationRegistry.getTargetContext();
File destDir = context.getExternalFilesDir(FEEDS_DIR);
assertNotNull(destDir);
@ -82,15 +82,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 +111,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,12 +143,8 @@ 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<>());

View File

@ -5,7 +5,6 @@ import android.test.FlakyTest;
import android.test.InstrumentationTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

View File

@ -21,6 +21,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
*/
class DBTestUtils {
private DBTestUtils(){}
/**
* Use this method when tests don't involve chapters.
*/

View File

@ -1,10 +1,14 @@
package de.test.antennapod.storage;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
import android.test.InstrumentationTestCase;
import android.util.Log;
import org.awaitility.Awaitility;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -15,15 +19,14 @@ 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;
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.PodDBAdapter;
import de.danoeh.antennapod.core.util.Consumer;
/**
* Test class for DBWriter
@ -58,6 +61,12 @@ public class DBWriterTest extends InstrumentationTestCase {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
Context context = getInstrumentation().getTargetContext();
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
prefEdit.putBoolean(UserPreferences.PREF_DELETE_REMOVES_FROM_QUEUE, true).commit();
UserPreferences.init(context);
}
public void testSetFeedMediaPlaybackInformation()
@ -124,89 +133,54 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNull(media.getFile_url());
}
public void testDeleteFeed() throws IOException, ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
public void testDeleteFeedMediaOfItemRemoveFromQueue()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
assertTrue(UserPreferences.shouldDeleteRemoveFromQueue());
File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile");
assertTrue(dest.createNewFile());
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
List<FeedItem> items = new ArrayList<>();
List<FeedItem> queue = new ArrayList<>();
feed.setItems(items);
FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.UNPLAYED, feed);
// 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);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0, 0);
item.setMedia(media);
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<>());
item.getChapters().add(new SimpleChapter(0, "item " + i, item, "example.com"));
}
items.add(item);
queue.add(item);
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.setQueue(queue);
adapter.close();
assertTrue(media.getId() != 0);
assertTrue(item.getId() != 0);
queue = DBReader.getQueue();
assertTrue(queue.size() != 0);
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();
DBWriter.deleteFeedMediaOfItem(getInstrumentation().getTargetContext(), media.getId());
Awaitility.await().until(() -> dest.exists() == false);
media = DBReader.getFeedMedia(media.getId());
assertNotNull(media);
assertFalse(dest.exists());
assertFalse(media.isDownloaded());
assertNull(media.getFile_url());
queue = DBReader.getQueue();
assertTrue(queue.size() == 0);
}
public void testDeleteFeedNoImage() throws ExecutionException, InterruptedException, IOException, 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<>());
feed.setImage(null);
List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
@ -261,13 +235,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 +243,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 +261,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 +276,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 +296,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 +303,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 +324,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 +346,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 +367,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 +388,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 +400,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);
@ -700,29 +575,16 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testRemoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", null, "title");
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.PLAYED, feed);
feed.getItems().add(item);
}
Feed feed = createTestFeed(NUM_ITEMS);
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
for (int removeIndex = 0; removeIndex < NUM_ITEMS; removeIndex++) {
final FeedItem item = feed.getItems().get(removeIndex);
adapter = PodDBAdapter.getInstance();
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setQueue(feed.getItems());
adapter.close();
DBWriter.removeQueueItem(context, item, false).get(TIMEOUT, TimeUnit.SECONDS);
DBWriter.removeQueueItem(context, false, item).get(TIMEOUT, TimeUnit.SECONDS);
adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor queue = adapter.getQueueIDCursor();
@ -742,6 +604,43 @@ public class DBWriterTest extends InstrumentationTestCase {
}
}
public void testRemoveQueueItemMultipleItems() throws InterruptedException, ExecutionException, TimeoutException {
// Setup test data
//
final int NUM_ITEMS = 5;
final int NUM_IN_QUEUE = NUM_ITEMS - 1; // the last one not in queue for boundary condition
final Context context = getInstrumentation().getTargetContext();
Feed feed = createTestFeed(NUM_ITEMS);
List<FeedItem> itemsToAdd = feed.getItems().subList(0, NUM_IN_QUEUE);
withPodDB(adapter -> adapter.setQueue(itemsToAdd) );
// Actual tests
//
// Use array rather than List to make codes more succinct
Long[] itemIds = toItemIds(feed.getItems()).toArray(new Long[0]);
DBWriter.removeQueueItem(context, false,
itemIds[1], itemIds[3]).get(TIMEOUT, TimeUnit.SECONDS);
assertQueueByItemIds("Average case - 2 items removed successfully",
itemIds[0], itemIds[2]);
DBWriter.removeQueueItem(context, false).get(TIMEOUT, TimeUnit.SECONDS);
assertQueueByItemIds("Boundary case - no items supplied. queue should see no change",
itemIds[0], itemIds[2]);
DBWriter.removeQueueItem(context, false,
itemIds[0], itemIds[4], -1L).get(TIMEOUT, TimeUnit.SECONDS);
assertQueueByItemIds("Boundary case - items not in queue ignored",
itemIds[2]);
DBWriter.removeQueueItem(context, false,
itemIds[2], -1L).get(TIMEOUT, TimeUnit.SECONDS);
assertQueueByItemIds("Boundary case - invalid itemIds ignored"); // the queue is empty
}
public void testMoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
@ -839,4 +738,53 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.isPlayed());
}
}
private static Feed createTestFeed(int numItems) {
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
withPodDB(adapter -> adapter.setCompleteFeed(feed));
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
return feed;
}
private static void withPodDB(Consumer<PodDBAdapter> action) {
PodDBAdapter adapter = PodDBAdapter.getInstance();
try {
adapter.open();
action.accept(adapter);
} finally {
adapter.close();
}
}
private static void assertQueueByItemIds(
String message,
long... itemIdsExpected
) {
List<FeedItem> queue = DBReader.getQueue();
List<Long> itemIdsActualList = toItemIds(queue);
List<Long> itemIdsExpectedList = new ArrayList<Long>(itemIdsExpected.length);
for (long id : itemIdsExpected) {
itemIdsExpectedList.add(id);
}
assertEquals(message, itemIdsExpectedList, itemIdsActualList);
}
private static List<Long> toItemIds(List<FeedItem> items) {
List<Long> itemIds = new ArrayList<Long>(items.size());
for(FeedItem item : items) {
itemIds.add(item.getId());
}
return itemIds;
}
}

View File

@ -2,16 +2,14 @@ package de.test.antennapod.ui;
import android.content.Context;
import android.content.SharedPreferences;
import android.test.ActivityInstrumentationTestCase2;
import android.test.FlakyTest;
import android.support.test.espresso.contrib.DrawerActions;
import android.support.test.espresso.intent.Intents;
import android.support.test.filters.FlakyTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.widget.ListView;
import com.robotium.solo.Solo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@ -23,25 +21,46 @@ 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 org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.NthMatcher.first;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
/**
* User interface tests for MainActivity
*/
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
private Solo solo;
private UITestUtils uiTestUtils;
private SharedPreferences prefs;
public MainActivityTest() {
super(MainActivity.class);
}
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
@Override
protected void setUp() throws Exception {
super.setUp();
Context context = getInstrumentation().getTargetContext();
@Before
public void setUp() throws IOException {
Intents.init();
Context context = mActivityRule.getActivity();
uiTestUtils = new UITestUtils(context);
uiTestUtils.setup();
@ -54,30 +73,26 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
// override first launch preference
// do this BEFORE calling getActivity()!
prefs = getInstrumentation().getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
prefs = context.getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
solo = new Solo(getInstrumentation(), getActivity());
solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
}
@Override
protected void tearDown() throws Exception {
@After
public void tearDown() throws Exception {
uiTestUtils.tearDown();
solo.finishOpenedActivities();
Intents.release();
PodDBAdapter.deleteDatabase();
// reset preferences
prefs.edit().clear().commit();
super.tearDown();
}
private void openNavDrawer() {
solo.clickOnImageButton(0);
getInstrumentation().waitForIdleSync();
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
@Test
public void testAddFeed() throws Exception {
uiTestUtils.addHostedFeedData();
final Feed feed = uiTestUtils.hostedFeeds.get(0);
@ -89,10 +104,12 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
solo.waitForView(R.id.butSubscribe);
assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
solo.clickOnButton(0);
solo.waitForText(solo.getString(R.string.subscribed_label));
assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
}
@FlakyTest(tolerance = 3)
@Test
@FlakyTest
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
@ -150,57 +167,60 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
@SuppressWarnings("unchecked")
@FlakyTest(tolerance = 3)
@Test
@FlakyTest
public void testGoToPreferences() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.waitForActivity(PreferenceActivity.class);
onView(withText(R.string.settings_label)).perform(click());
intended(hasComponent(PreferenceActivity.class.getName()));
}
@Test
public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.episodes_label));
solo.clickOnText(solo.getString(R.string.playback_history_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
onView(first(withText(R.string.queue_label))).perform(longClick());
onView(withText(R.string.episodes_label)).perform(click());
onView(withText(R.string.playback_history_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(EpisodesFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
@Test
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
UserPreferences.setHiddenDrawerItems(hidden);
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.downloads_label));
solo.clickOnText(solo.getString(R.string.queue_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
onView(first(withText(R.string.queue_label))).perform(longClick());
onView(withText(R.string.downloads_label)).perform(click());
onView(withText(R.string.queue_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(QueueFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
@Test
public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles);
String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
onView(first(withText(R.string.queue_label))).perform(longClick());
for (String title : titles) {
solo.clickOnText(title);
onView(first(withText(title))).perform(click());
}
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(titles.length, hidden.size());
for (String tag : MainActivity.NAV_DRAWER_TAGS) {
@ -208,21 +228,85 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
}
@Test
public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
onView(first(withText(R.string.downloads_label))).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
openNavDrawer();
String downloads = solo.getString(R.string.downloads_label);
solo.clickOnText(downloads);
solo.waitForView(android.R.id.list);
openNavDrawer();
solo.clickLongOnText(downloads);
solo.waitForDialogToOpen();
solo.clickOnText(downloads);
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(1, hidden.size());
assertTrue(hidden.contains(DownloadsFragment.TAG));
}
@Test
public void testBackButtonBehaviorGoToPage() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.subscriptions_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.goBackToActivity(MainActivity.class.getSimpleName());
solo.goBack();
assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
}
@Test
public void testBackButtonBehaviorOpenDrawer() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
solo.clickOnText(solo.getString(R.string.back_button_open_drawer));
solo.goBackToActivity(MainActivity.class.getSimpleName());
solo.goBack();
assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen());
}
@Test
public void testBackButtonBehaviorDoubleTap() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
solo.clickOnText(solo.getString(R.string.back_button_double_tap));
solo.goBackToActivity(MainActivity.class.getSimpleName());
solo.goBack();
solo.goBack();
assertTrue(solo.getCurrentActivity().isFinishing());
}
@Test
public void testBackButtonBehaviorPrompt() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
solo.clickOnText(solo.getString(R.string.back_button_show_prompt));
solo.goBackToActivity(MainActivity.class.getSimpleName());
solo.goBack();
solo.clickOnText(solo.getString(R.string.yes));
solo.waitForDialogToClose();
assertTrue(solo.getCurrentActivity().isFinishing());
}
@Test
public void testBackButtonBehaviorDefault() {
openNavDrawer();
solo.clickOnText(solo.getString(R.string.settings_label));
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
solo.clickOnText(solo.getString(R.string.back_button_default));
solo.goBackToActivity(MainActivity.class.getSimpleName());
solo.goBack();
assertTrue(solo.getCurrentActivity().isFinishing());
}
}

View File

@ -59,7 +59,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
.clear()
.putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
.putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
.putBoolean(UserPreferences.PREF_SONIC, true)
.putString(UserPreferences.PREF_MEDIA_PLAYER, "sonic")
.commit();
solo = new Solo(getInstrumentation(), getActivity());

View File

@ -1,12 +1,23 @@
package de.test.antennapod.ui;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.test.ActivityInstrumentationTestCase2;
import android.preference.PreferenceManager;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@ -17,36 +28,48 @@ import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
public class PreferencesTest extends ActivityInstrumentationTestCase2<PreferenceActivity> {
private static final String TAG = "PreferencesTest";
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
@RunWith(AndroidJUnit4.class)
public class PreferencesTest {
private Solo solo;
private Context context;
private Resources res;
private SharedPreferences prefs;
public PreferencesTest() {
super(PreferenceActivity.class);
}
@Rule
public ActivityTestRule<PreferenceActivity> mActivityRule = new ActivityTestRule<>(PreferenceActivity.class);
@Override
public void setUp() throws Exception {
super.setUp();
solo = new Solo(getInstrumentation(), getActivity());
@Before
public void setUp() {
solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
Timeout.setSmallTimeout(500);
Timeout.setLargeTimeout(1000);
context = getInstrumentation().getTargetContext();
res = getActivity().getResources();
UserPreferences.init(context);
res = mActivityRule.getActivity().getResources();
UserPreferences.init(mActivityRule.getActivity());
prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
prefs.edit().clear();
prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
}
@Override
public void tearDown() throws Exception {
@After
public void tearDown() {
solo.finishOpenedActivities();
super.tearDown();
prefs.edit().clear();
}
@Test
public void testSwitchTheme() {
final int theme = UserPreferences.getTheme();
int otherTheme;
@ -55,13 +78,13 @@ 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));
clickPreference(withText(R.string.user_interface_label));
clickPreference(withText(R.string.pref_set_theme_title));
onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
@Test
public void testSwitchThemeBack() {
final int theme = UserPreferences.getTheme();
int otherTheme;
@ -70,33 +93,23 @@ 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));
clickPreference(withText(R.string.user_interface_label));
clickPreference(withText(R.string.pref_set_theme_title));
onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
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()));
solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
assertTrue(solo.waitForCondition(() -> priority == UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
}
@Test
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));
clickPreference(withText(R.string.user_interface_label));
clickPreference(withText(R.string.pref_persistNotify_title));
assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
clickPreference(withText(R.string.pref_persistNotify_title));
assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
}
@Test
public void testSetLockscreenButtons() {
solo.clickOnText(solo.getString(R.string.user_interface_label));
solo.scrollDown();
@ -123,6 +136,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
}
@Test
public void testEnqueueAtFront() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
@ -134,6 +148,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
}
@Test
public void testHeadPhonesDisconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
@ -143,6 +158,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testHeadPhonesReconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
@ -156,6 +172,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testBluetoothReconnect() {
solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
@ -169,6 +186,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
}
@Test
public void testContinuousPlayback() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean continuousPlayback = UserPreferences.isFollowQueue();
@ -180,6 +198,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
}
@Test
public void testAutoDelete() {
solo.clickOnText(solo.getString(R.string.storage_pref));
final boolean autoDelete = UserPreferences.isAutoDelete();
@ -189,17 +208,15 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
}
@Test
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);
clickPreference(withText(R.string.playback_pref));
clickPreference(withText(R.string.pref_playback_speed_title));
assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0]));
solo.clickOnText(solo.getString(R.string.cancel_label));
solo.waitForDialogToClose(1000);
onView(withText(R.string.cancel_label)).perform(click());
}
@Test
public void testPauseForInterruptions() {
solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
@ -209,6 +226,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
}
@Test
public void testDisableUpdateInterval() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
@ -217,31 +235,31 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
}
@Test
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));
solo.waitForDialogToOpen();
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_autoUpdateIntervallOrTime_title));
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Interval)).perform(click());
String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
solo.clickOnText(search);
solo.waitForDialogToClose();
onView(withText(search)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
}
@Test
public void testMobileUpdates() {
solo.clickOnText(solo.getString(R.string.network_pref));
clickPreference(withText(R.string.network_pref));
final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate();
solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
clickPreference(withText(R.string.pref_mobileUpdate_title));
assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
clickPreference(withText(R.string.pref_mobileUpdate_title));
assertTrue(solo.waitForCondition(() -> mobileUpdates == UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
}
@Test
public void testSetSequentialDownload() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "1");
@ -249,9 +267,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
}
@Test
public void testSetParallelDownloads() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "10");
@ -259,50 +278,50 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
}
@Test
public void testSetParallelDownloadsInvalidInput() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "0");
assertEquals("1", solo.getEditText(0).getText().toString());
assertEquals("", solo.getEditText(0).getText().toString());
solo.clearEditText(0);
solo.enterText(0, "100");
assertEquals("50", solo.getEditText(0).getText().toString());
assertEquals("", solo.getEditText(0).getText().toString());
}
@Test
public void testSetEpisodeCache() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
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));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_automatic_download_title));
clickPreference(withText(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(entry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
}
@Test
public void testSetEpisodeCacheMin() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
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()) {
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
}
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_automatic_download_title));
clickPreference(withText(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen(1000);
solo.scrollUp();
solo.clickOnText(minEntry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
}
@Test
public void testSetEpisodeCacheMax() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
@ -311,24 +330,22 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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()) {
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
}
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(maxEntry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
}
@Test
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));
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_automatic_download_title));
clickPreference(withText(R.string.pref_automatic_download_title));
assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
if(UserPreferences.isEnableAutodownload() == false) {
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
clickPreference(withText(R.string.pref_automatic_download_title));
}
assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
@ -343,6 +360,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
}
@Test
public void testEpisodeCleanupQueueOnly() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
@ -356,6 +374,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
@Test
public void testEpisodeCleanupNeverAlg() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
@ -369,6 +388,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
@Test
public void testEpisodeCleanupClassic() {
solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
@ -379,39 +399,39 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
return cleanupAlg.getNumberOfDaysAfterPlayback() == 0;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
}
return false;
},
Timeout.getLargeTimeout()));
}
@Test
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");
clickPreference(withText(R.string.network_pref));
clickPreference(withText(R.string.pref_automatic_download_title));
clickPreference(withText(R.string.pref_episode_cleanup_title));
solo.waitForDialogToOpen();
String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
onView(withText(search)).perform(click());
assertTrue(solo.waitForCondition(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
return cleanupAlg.getNumberOfDaysAfterPlayback() == 5;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 120; // 5 days
}
return false;
},
Timeout.getLargeTimeout()));
}
@Test
public void testRewindChange() {
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));
clickPreference(withText(R.string.playback_pref));
clickPreference(withText(R.string.pref_rewind));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
@ -419,24 +439,22 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
solo.clickOnButton("Confirm");
onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
@Test
public void testFastForwardChange() {
solo.clickOnText(solo.getString(R.string.playback_pref));
solo.scrollDown();
solo.scrollDown();
clickPreference(withText(R.string.playback_pref));
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
solo.clickOnText(solo.getString(R.string.pref_fast_forward));
clickPreference(withText(R.string.pref_fast_forward));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
@ -445,12 +463,66 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
solo.clickOnButton("Confirm");
onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
}
@Test
public void testBackButtonBehaviorGoToPageSelector() {
clickPreference(withText(R.string.user_interface_label));
clickPreference(withText(R.string.pref_back_button_behavior_title));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.queue_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
Timeout.getLargeTimeout()));
clickPreference(withText(R.string.pref_back_button_behavior_title));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.episodes_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
Timeout.getLargeTimeout()));
clickPreference(withText(R.string.pref_back_button_behavior_title));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.subscriptions_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
Timeout.getLargeTimeout()));
}
@Test
public void testDeleteRemovesFromQueue() {
clickPreference(withText(R.string.storage_pref));
if (!UserPreferences.shouldDeleteRemoveFromQueue()) {
clickPreference(withText(R.string.pref_delete_removes_from_queue_title));
assertTrue(solo.waitForCondition(UserPreferences::shouldDeleteRemoveFromQueue, Timeout.getLargeTimeout()));
}
final boolean deleteRemovesFromQueue = UserPreferences.shouldDeleteRemoveFromQueue();
solo.clickOnText(solo.getString(R.string.pref_delete_removes_from_queue_title));
assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_delete_removes_from_queue_title));
assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
}
private void clickPreference(Matcher<View> matcher) {
onView(withId(R.id.list))
.perform(RecyclerViewActions.actionOnItem(hasDescendant(matcher), click()));
}
}

View File

@ -22,7 +22,6 @@ 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.storage.PodDBAdapter;
@ -136,12 +135,9 @@ 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<>();
@ -187,12 +183,6 @@ 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,35 +0,0 @@
package de.test.antennapod.util;
import android.test.AndroidTestCase;
import de.danoeh.antennapod.core.util.Converter;
/**
* Test class for converter
*/
public class ConverterTest extends AndroidTestCase {
public void testGetDurationStringLong() throws Exception {
String expected = "13:05:10";
int input = 47110000;
assertEquals(expected, Converter.getDurationStringLong(input));
}
public void testGetDurationStringShort() throws Exception {
String expected = "13:05";
int input = 47110000;
assertEquals(expected, Converter.getDurationStringShort(input));
}
public void testDurationStringLongToMs() throws Exception {
String input = "01:20:30";
long expected = 4830000;
assertEquals(expected, Converter.durationStringLongToMs(input));
}
public void testDurationStringShortToMs() throws Exception {
String input = "8:30";
long expected = 30600000;
assertEquals(expected, Converter.durationStringShortToMs(input));
}
}

View File

@ -30,12 +30,12 @@ public class TimelineTest extends InstrumentationTestCase {
context = getInstrumentation().getTargetContext();
}
private Playable newTestPlayable(List<Chapter> chapters, String shownotes) {
private Playable newTestPlayable(List<Chapter> chapters, String shownotes, int duration) {
FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
item.setChapters(chapters);
item.setContentEncoded(shownotes);
FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
media.setDuration(Integer.MAX_VALUE);
media.setDuration(duration);
item.setMedia(media);
return media;
}
@ -44,7 +44,17 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11:12";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAddTimecodeHHMMSSMoreThen24HoursNoChapters() throws Exception {
final String timeStr = "25:00:00";
final long time = 25 * 60 * 60 * 1000;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@ -54,17 +64,67 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAddTimecodeMMSSNoChapters() throws Exception {
final String timeStr = "10:11";
final long time = 10 * 60 * 1000 + 11 * 1000;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", 11 * 60 * 1000);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAddTimecodeHMMSSNoChapters() throws Exception {
final String timeStr = "2:11:12";
final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAddTimecodeMSSNoChapters() throws Exception {
final String timeStr = "1:12";
final long time = 60 * 1000 + 12 * 1000;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>", 2 * 60 * 1000);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() throws Exception {
final String[] timeStrings = new String[]{ "10:12", "1:10:12" };
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 2 * 60 * 60 * 1000);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000 }, timeStrings);
}
public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() throws Exception {
// One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS.
final String[] timeStrings = new String[]{ "10:12", "2:12" };
Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 3 * 60 * 60 * 1000);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000 }, timeStrings);
}
public void testProcessShownotesAddTimecodeParentheses() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + timeStr + ") here.</p>");
Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + timeStr + ") here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@ -74,7 +134,7 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + timeStr + "] here.</p>");
Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + timeStr + "] here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@ -84,12 +144,27 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + timeStr + "> here.</p>");
Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + timeStr + "> here.</p>", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
public void testProcessShownotesAndInvalidTimecode() throws Exception {
final String[] timeStrs = new String[] {"2:1", "0:0", "000", "00", "00:000"};
StringBuilder shownotes = new StringBuilder("<p> Some test text with timecodes ");
for (String timeStr : timeStrs) {
shownotes.append(timeStr).append(" ");
}
shownotes.append("here.</p>");
Playable p = newTestPlayable(null, shownotes.toString(), Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[0], new String[0]);
}
private void checkLinkCorrect(String res, long[] timecodes, String[] timecodeStr) {
assertNotNull(res);
Document d = Jsoup.parse(res);

View File

@ -56,6 +56,11 @@ public class AtomGenerator implements FeedGenerator{
xml.text(feed.getDescription());
xml.endTag(null, "subtitle");
}
if (feed.getImageUrl() != null) {
xml.startTag(null, "logo");
xml.text(feed.getImageUrl());
xml.endTag(null, "logo");
}
if (feed.getPaymentLink() != null) {
GeneratorUtil.addPaymentLink(xml, feed.getPaymentLink(), false);

View File

@ -8,6 +8,7 @@ import java.io.IOException;
* Utility methods for FeedGenerator
*/
class GeneratorUtil {
private GeneratorUtil(){}
public static void addPaymentLink(XmlSerializer xml, String paymentLink, boolean withNamespace) throws IOException {
String ns = (withNamespace) ? "http://www.w3.org/2005/Atom" : null;

View File

@ -54,6 +54,13 @@ public class RSS2Generator implements FeedGenerator{
xml.text(feed.getLanguage());
xml.endTag(null, "language");
}
if (feed.getImageUrl() != null) {
xml.startTag(null, "image");
xml.startTag(null, "url");
xml.text(feed.getImageUrl());
xml.endTag(null, "url");
xml.endTag(null, "image");
}
if (feed.getPaymentLink() != null) {
GeneratorUtil.addPaymentLink(xml, feed.getPaymentLink(), true);

1
app/src/free/play Symbolic link
View File

@ -0,0 +1 @@
../main/play

View File

@ -1,14 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
android:installLocation="auto"
android:versionCode="1060595"
android:versionName="1.6.5">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
"1.2.3-RC4" -> 1020304
-->
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
@ -47,10 +41,13 @@
<activity
android:name=".activity.SplashActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/Theme.AntennaPod.Dark.Splash">
<!-- android:launchMode="singleTask" removed for #2948, so that
when app is launched again, the app will go to the last activity users use
(if the app has been used recently, e.g., last 30 minutes),
rather than always go to MainActivity (launched by SplashActivity here) -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -391,10 +388,6 @@
android:resource="@xml/provider_paths"/>
</provider>
<meta-data
android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
android:value="GlideModule" />
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>

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

@ -20,7 +20,7 @@ public class PodcastApp extends Application {
try {
Class.forName("de.danoeh.antennapod.config.ClientConfigurator");
} catch (Exception e) {
throw new RuntimeException("ClientConfigurator not found");
throw new RuntimeException("ClientConfigurator not found", e);
}
}

View File

@ -21,10 +21,10 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import rx.Single;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'about' screen
@ -35,7 +35,7 @@ public class AboutActivity extends AppCompatActivity {
private WebView webView;
private LinearLayout webViewContainer;
private Subscription subscription;
private Disposable disposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -43,8 +43,8 @@ public class AboutActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.about);
webViewContainer = (LinearLayout) findViewById(R.id.webViewContainer);
webView = (WebView) findViewById(R.id.webViewAbout);
webViewContainer = findViewById(R.id.webViewContainer);
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 <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
@ -69,13 +69,16 @@ public class AboutActivity extends AppCompatActivity {
}
private void loadAsset(String filename) {
subscription = Single.create(subscriber -> {
disposable = Single.create(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);
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());
@ -92,7 +95,7 @@ public class AboutActivity extends AppCompatActivity {
" src: url('file:///android_asset/Roboto-Light.ttf');" +
" }" +
" * {" +
" color: %s;" +
" color: @fontcolor@;" +
" font-family: roboto-Light;" +
" font-size: 8pt;" +
" }" +
@ -100,7 +103,10 @@ public class AboutActivity extends AppCompatActivity {
"</head><body><p>" + webViewData + "</p></body></html>";
webViewData = webViewData.replace("\n", "<br/>");
}
webViewData = String.format(webViewData, colorString);
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));
@ -109,7 +115,7 @@ public class AboutActivity extends AppCompatActivity {
IOUtils.closeQuietly(input);
}
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
webViewData ->
@ -140,8 +146,8 @@ public class AboutActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
if (webViewContainer != null && webView != null) {
webViewContainer.removeAllViews();

View File

@ -12,8 +12,6 @@ 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.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/**
@ -28,20 +26,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
protected void onResume() {
super.onResume();
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent();
if (intent.getData() == null) {
return;
}
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
MediaType.AUDIO);
new PlaybackServiceStarter(this, media)
.startWhenPrepared(true)
.shouldStream(false)
.prepareImmediately(true)
.start();
playExternalMedia(getIntent(), MediaType.AUDIO);
} else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (intent.getComponent() != null &&

View File

@ -64,11 +64,11 @@ public class DirectoryChooserActivity extends AppCompatActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.directory_chooser);
butConfirm = (Button) findViewById(R.id.butConfirm);
butCancel = (Button) findViewById(R.id.butCancel);
butNavUp = (ImageButton) findViewById(R.id.butNavUp);
txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
listDirectories = (ListView) findViewById(R.id.directory_list);
butConfirm = findViewById(R.id.butConfirm);
butCancel = findViewById(R.id.butCancel);
butNavUp = findViewById(R.id.butNavUp);
txtvSelectedFolder = findViewById(R.id.txtvSelectedFolder);
listDirectories = findViewById(R.id.directory_list);
butConfirm.setOnClickListener(new OnClickListener() {

View File

@ -49,11 +49,11 @@ public class DownloadAuthenticationActivity extends AppCompatActivity {
}
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);
Button butConfirm = (Button) findViewById(R.id.butConfirm);
Button butCancel = (Button) findViewById(R.id.butCancel);
TextView txtvDescription = findViewById(R.id.txtvDescription);
etxtUsername = findViewById(R.id.etxtUsername);
etxtPassword = findViewById(R.id.etxtPassword);
Button butConfirm = findViewById(R.id.butConfirm);
Button butCancel = findViewById(R.id.butCancel);
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);

View File

@ -16,8 +16,15 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
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.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
@ -30,13 +37,11 @@ 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;
import rx.schedulers.Schedulers;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a feed.
@ -56,7 +61,7 @@ public class FeedInfoActivity extends AppCompatActivity {
private TextView txtvAuthor;
private TextView txtvUrl;
private Subscription subscription;
private Disposable disposable;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@ -82,52 +87,57 @@ public class FeedInfoActivity extends AppCompatActivity {
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);
imgvCover = findViewById(R.id.imgvCover);
txtvTitle = findViewById(R.id.txtvTitle);
TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor);
ImageView imgvBackground = 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.txtvDetailsAuthor);
txtvUrl = (TextView) findViewById(R.id.txtvUrl);
txtvDescription = findViewById(R.id.txtvDescription);
lblLanguage = findViewById(R.id.lblLanguage);
txtvLanguage = findViewById(R.id.txtvLanguage);
lblAuthor = findViewById(R.id.lblAuthor);
txtvAuthor = findViewById(R.id.txtvDetailsAuthor);
txtvUrl = findViewById(R.id.txtvUrl);
txtvUrl.setOnClickListener(copyUrlToClipboard);
subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId))
.subscribeOn(Schedulers.newThread())
disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
Feed feed = DBReader.getFeed(feedId);
if (feed != null) {
emitter.onSuccess(feed);
} else {
emitter.onComplete();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result == null) {
Log.e(TAG, "Activity was started with invalid arguments");
finish();
}
feed = result;
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.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()
.apply(new RequestOptions()
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation())
.dontAnimate())
.into(imgvBackground);
txtvTitle.setText(feed.getTitle());
@ -164,14 +174,17 @@ public class FeedInfoActivity extends AppCompatActivity {
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
}, () -> {
Log.e(TAG, "Activity was started with invalid arguments");
finish();
});
}
@Override
public void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -24,8 +23,10 @@ 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.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
@ -40,10 +41,11 @@ 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;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a feed.
@ -67,23 +69,7 @@ public class FeedSettingsActivity extends AppCompatActivity {
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 Disposable disposable;
private boolean authInfoChanged = false;
@ -127,57 +113,62 @@ public class FeedSettingsActivity extends AppCompatActivity {
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);
imgvCover = findViewById(R.id.imgvCover);
txtvTitle = findViewById(R.id.txtvTitle);
TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor);
ImageView imgvBackground = 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);
cbxAutoDownload = findViewById(R.id.cbxAutoDownload);
cbxKeepUpdated = findViewById(R.id.cbxKeepUpdated);
spnAutoDelete = findViewById(R.id.spnAutoDelete);
etxtUsername = findViewById(R.id.etxtUsername);
etxtPassword = findViewById(R.id.etxtPassword);
etxtFilterText = findViewById(R.id.etxtEpisodeFilterText);
rdoFilterInclude = findViewById(R.id.radio_filter_include);
rdoFilterInclude.setOnClickListener(v -> {
filterInclude = true;
filterTextChanged = true;
});
rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude);
rdoFilterExclude = findViewById(R.id.radio_filter_exclude);
rdoFilterExclude.setOnClickListener(v -> {
filterInclude = false;
filterTextChanged = true;
});
subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId))
.subscribeOn(Schedulers.newThread())
disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
Feed feed = DBReader.getFeed(feedId);
if (feed != null) {
emitter.onSuccess(feed);
} else {
emitter.onComplete();
}
})
.subscribeOn(Schedulers.io())
.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()
.apply(new RequestOptions()
.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()
.apply(new RequestOptions()
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation())
.dontAnimate())
.into(imgvBackground);
txtvTitle.setText(feed.getTitle());
@ -259,6 +250,9 @@ public class FeedSettingsActivity extends AppCompatActivity {
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
}, () -> {
Log.e(TAG, "Activity was started with invalid arguments");
finish();
});
}
@ -296,8 +290,8 @@ public class FeedSettingsActivity extends AppCompatActivity {
@Override
public void onDestroy() {
super.onDestroy();
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}

View File

@ -41,9 +41,9 @@ public class FlattrAuthActivity extends AppCompatActivity {
if (BuildConfig.DEBUG) Log.d(TAG, "Activity created");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.flattr_auth);
txtvExplanation = (TextView) findViewById(R.id.txtvExplanation);
butAuthenticate = (Button) findViewById(R.id.but_authenticate);
butReturn = (Button) findViewById(R.id.but_return_home);
txtvExplanation = findViewById(R.id.txtvExplanation);
butAuthenticate = findViewById(R.id.but_authenticate);
butReturn = findViewById(R.id.but_return_home);
butReturn.setOnClickListener(v -> {
Intent intent = new Intent(FlattrAuthActivity.this, MainActivity.class);

View File

@ -13,9 +13,7 @@ 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;
@ -26,6 +24,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
/**
* Displays the 'import/export' screen
*/

View File

@ -27,11 +27,10 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
@ -44,17 +43,20 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@ -67,10 +69,10 @@ import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* The activity that is shown when the user launches the app.
@ -120,7 +122,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private ProgressDialog pd;
private Subscription subscription;
private Disposable disposable;
private long lastBackButtonPressTime = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -129,7 +133,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -141,8 +145,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
currentTitle = getTitle();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
drawerLayout = findViewById(R.id.drawer_layout);
navList = findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
@ -470,7 +474,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
protected void onResume() {
super.onResume();
StorageUtils.checkStorageAvailability(this);
DBTasks.checkShouldRefreshFeeds(getApplicationContext());
AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
Intent intent = getIntent();
if (intent.hasExtra(EXTRA_FEED_ID) ||
@ -487,8 +491,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
if(pd != null) {
pd.dismiss();
@ -627,8 +631,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();
@ -643,10 +646,40 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public void onBackPressed() {
if(isDrawerOpen()) {
if (isDrawerOpen()) {
drawerLayout.closeDrawer(navDrawer);
} else {
} else if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
super.onBackPressed();
} else {
switch (UserPreferences.getBackButtonBehavior()) {
case OPEN_DRAWER:
drawerLayout.openDrawer(navDrawer);
break;
case SHOW_PROMPT:
new AlertDialog.Builder(this)
.setMessage(R.string.close_prompt)
.setPositiveButton(R.string.yes, (dialogInterface, i) -> MainActivity.super.onBackPressed())
.setNegativeButton(R.string.no, null)
.setCancelable(false)
.show();
break;
case DOUBLE_TAP:
if (lastBackButtonPressTime < System.currentTimeMillis() - 2000) {
Toast.makeText(this, R.string.double_tap_toast, Toast.LENGTH_SHORT).show();
lastBackButtonPressTime = System.currentTimeMillis();
} else {
super.onBackPressed();
}
break;
case GO_TO_PAGE:
if (getLastNavFragment().equals(UserPreferences.getBackButtonGoToPage())) {
super.onBackPressed();
} else {
loadFragment(UserPreferences.getBackButtonGoToPage(), null);
}
break;
default: super.onBackPressed();
}
}
}
@ -717,8 +750,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
};
private void loadData() {
subscription = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
boolean handleIntent = (navDrawerData == null);

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,7 +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;
@ -37,28 +41,33 @@ 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.Consumer;
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.Function;
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;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
@ -69,6 +78,10 @@ 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;
private static final float PLAYBACK_SPEED_STEP = 0.05f;
private static final float DEFAULT_MIN_PLAYBACK_SPEED = 0.5f;
private static final float DEFAULT_MAX_PLAYBACK_SPEED = 2.5f;
PlaybackController controller;
@ -86,6 +99,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private boolean isFavorite = false;
private Disposable disposable;
private PlaybackController newPlaybackController() {
return new PlaybackController(this, false) {
@ -222,8 +237,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Log.d(TAG, "onCreate()");
StorageUtils.checkStorageAvailability(this);
orientation = getResources().getConfiguration().orientation;
getWindow().setFormat(PixelFormat.TRANSPARENT);
setupGUI();
loadMediaInfo();
}
@Override
@ -256,15 +272,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private void onBufferUpdate(float progress) {
if (sbPosition != null) {
sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
}
}
/**
* Current screen orientation.
*/
private int orientation;
@Override
protected void onStart() {
super.onStart();
@ -272,8 +283,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
}
controller = newPlaybackController();
setupGUI();
loadMediaInfo();
onPositionObserverUpdate();
}
@ -284,6 +293,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
controller = null; // prevent leak
}
if (disposable != null) {
disposable.dispose();
}
super.onStop();
}
@ -460,7 +472,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
butDecSpeed.setOnClickListener(v -> {
if(controller != null && controller.canSetPlaybackSpeed()) {
barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2);
barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 1);
} else {
VariableSpeedDialog.showGetPluginDialog(this);
}
@ -468,7 +480,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
butIncSpeed.setOnClickListener(v -> {
if(controller != null && controller.canSetPlaybackSpeed()) {
barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2);
barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 1);
} else {
VariableSpeedDialog.showGetPluginDialog(this);
}
@ -483,12 +495,20 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed));
}
String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
final float minPlaybackSpeed = availableSpeeds.length > 1 ?
Float.valueOf(availableSpeeds[0]) : DEFAULT_MIN_PLAYBACK_SPEED;
float maxPlaybackSpeed = availableSpeeds.length > 1 ?
Float.valueOf(availableSpeeds[availableSpeeds.length - 1]) : DEFAULT_MAX_PLAYBACK_SPEED;
int progressMax = (int) ((maxPlaybackSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP);
barPlaybackSpeed.setMax(progressMax);
txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(controller != null && controller.canSetPlaybackSpeed()) {
float playbackSpeed = (progress + 10) / 20.0f;
float playbackSpeed = progress * PLAYBACK_SPEED_STEP + minPlaybackSpeed;
controller.setPlaybackSpeed(playbackSpeed);
String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
UserPreferences.setPlaybackSpeed(speedPref);
@ -496,7 +516,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
txtvPlaybackSpeed.setText(speedStr);
} else if(fromUser) {
float speed = Float.valueOf(UserPreferences.getPlaybackSpeed());
barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress((int) (20 * speed) - 10));
barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(
(int) ((speed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)));
}
}
@ -511,7 +532,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10);
barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
@ -525,6 +546,22 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
}
if (UserPreferences.useExoplayer()) {
barRightVolume.setEnabled(false);
}
final CheckBox skipSilence = (CheckBox) dialog.findViewById(R.id.skipSilence);
skipSilence.setChecked(UserPreferences.isSkipSilence());
if (!UserPreferences.useExoplayer()) {
skipSilence.setEnabled(false);
String exoplayerOnly = getString(R.string.exoplayer_only);
skipSilence.setText(skipSilence.getText() + " [" + exoplayerOnly + "]");
}
skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
UserPreferences.setSkipSilence(isChecked);
controller.setSkipSilence(isChecked);
});
barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@ -635,7 +672,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (controller != null) {
controller.init();
}
}
}
@ -690,7 +726,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
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();
@ -722,8 +757,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
R.string.pref_rewind);
private final Supplier<Integer> getPrefSecsFn;
private final Func1<MediaplayerActivity, TextView> getTextViewFn;
private final Action1<Integer> setPrefSecsFn;
private final Function<MediaplayerActivity, TextView> getTextViewFn;
private final Consumer<Integer> setPrefSecsFn;
private final int titleResourceID;
/**
@ -735,7 +770,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* @param setPrefSecsFn Handle to function that sets the preference (setting) for the skip delta value (and optionally updates the button label with the current values)
* @param titleResourceID ID of the resource string with the title for a view
*/
SkipDirection(Supplier<Integer> getPrefSecsFn, Func1<MediaplayerActivity, TextView> getTextViewFn, Action1<Integer> setPrefSecsFn, int titleResourceID) {
SkipDirection(Supplier<Integer> getPrefSecsFn, Function<MediaplayerActivity, TextView> getTextViewFn, Consumer<Integer> setPrefSecsFn, int titleResourceID) {
this.getPrefSecsFn = getPrefSecsFn;
this.getTextViewFn = getTextViewFn;
this.setPrefSecsFn = setPrefSecsFn;
@ -754,10 +789,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* @param activity MediaplyerActivity that contains textview to update the display of the skip delta setting (or null if nothing to update)
*/
public void setPrefSkipSeconds(int seconds, @Nullable Activity activity) {
setPrefSecsFn.call(seconds);
setPrefSecsFn.accept(seconds);
if (activity != null && activity instanceof MediaplayerActivity) {
TextView tv = getTextViewFn.call((MediaplayerActivity)activity);
TextView tv = getTextViewFn.apply((MediaplayerActivity)activity);
if (tv != null) tv.setText(String.valueOf(seconds));
}
}
@ -795,13 +830,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
void setupGUI() {
setContentView(getContentViewResourceId());
sbPosition = (SeekBar) findViewById(R.id.sbPosition);
txtvPosition = (TextView) findViewById(R.id.txtvPosition);
sbPosition = findViewById(R.id.sbPosition);
txtvPosition = findViewById(R.id.txtvPosition);
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
Log.d("timeleft", showTimeLeft ? "true" : "false");
txtvLength = (TextView) findViewById(R.id.txtvLength);
txtvLength = findViewById(R.id.txtvLength);
if (txtvLength != null) {
txtvLength.setOnClickListener(v -> {
showTimeLeft = !showTimeLeft;
@ -825,18 +860,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
});
}
butRev = (ImageButton) findViewById(R.id.butRev);
txtvRev = (TextView) findViewById(R.id.txtvRev);
butRev = findViewById(R.id.butRev);
txtvRev = findViewById(R.id.txtvRev);
if (txtvRev != null) {
txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
}
butPlay = (ImageButton) findViewById(R.id.butPlay);
butFF = (ImageButton) findViewById(R.id.butFF);
txtvFF = (TextView) findViewById(R.id.txtvFF);
butPlay = findViewById(R.id.butPlay);
butFF = findViewById(R.id.butFF);
txtvFF = findViewById(R.id.txtvFF);
if (txtvFF != null) {
txtvFF.setText(String.valueOf(UserPreferences.getFastForwardSecs()));
}
butSkip = (ImageButton) findViewById(R.id.butSkip);
butSkip = findViewById(R.id.butSkip);
// SEEKBAR SETUP
@ -863,7 +898,8 @@ 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));
}
}
@ -937,22 +973,62 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private void checkFavorite() {
Playable playable = controller.getMedia();
if (playable != null && playable instanceof FeedMedia) {
FeedItem feedItem = ((FeedMedia) playable).getItem();
if (feedItem != null) {
Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
item -> {
boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
if (isFavorite != isFav) {
isFavorite = isFav;
invalidateOptionsMenu();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
if (!(playable instanceof FeedMedia)) {
return;
}
FeedItem feedItem = ((FeedMedia) playable).getItem();
if (feedItem == null) {
return;
}
if (disposable != null) {
disposable.dispose();
}
disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
item -> {
boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
if (isFavorite != isFav) {
isFavorite = isFav;
invalidateOptionsMenu();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
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

@ -46,8 +46,9 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
@ -62,10 +63,10 @@ import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Activity for playing files that do not require a video surface.
@ -105,7 +106,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private ViewPager pager;
private MediaplayerInfoPagerAdapter pagerAdapter;
private Subscription subscription;
private Disposable disposable;
@Override
protected void onPause() {
@ -126,8 +127,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if(pagerAdapter != null) {
pagerAdapter.setController(null);
}
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
saveCurrentFragment();
@ -186,7 +187,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pagerAdapter.onMediaChanged(media);
pagerAdapter.setController(controller);
}
DBTasks.checkShouldRefreshFeeds(getApplicationContext());
AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
@ -226,18 +227,18 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void setupGUI() {
super.setupGUI();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
findViewById(R.id.shadow).setVisibility(View.GONE);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar);
AppBarLayout appBarLayout = findViewById(R.id.appBar);
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
appBarLayout.setElevation(px);
}
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
drawerLayout = findViewById(R.id.drawer_layout);
navList = findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
@ -273,14 +274,15 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class));
});
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
butCastDisconnect = (ImageButton) findViewById(R.id.butCastDisconnect);
butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
butCastDisconnect = findViewById(R.id.butCastDisconnect);
pager = (ViewPager) findViewById(R.id.pager);
pager = findViewById(R.id.pager);
pager.setOffscreenPageLimit(3);
pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager(), media);
pagerAdapter.setController(controller);
pager.setAdapter(pagerAdapter);
CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator);
CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator);
pageIndicator.setViewPager(pager);
loadLastFragment();
pager.onSaveInstanceState();
@ -356,7 +358,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return drawerToggle != null && drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
return (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
}
@Override
@ -413,8 +415,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
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);
}
}
}
@ -472,8 +473,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private DBReader.NavDrawerData navDrawerData;
private void loadData() {
subscription = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
@ -27,6 +28,7 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
@ -56,19 +58,21 @@ 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;
import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Downloads a feed from a feed URL and parses it. Subclasses can display the
@ -97,15 +101,15 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private Button subscribeButton;
private Subscription download;
private Subscription parser;
private Subscription updater;
private Disposable download;
private Disposable parser;
private Disposable updater;
private final EventDistributor.EventListener listener = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
updater = Observable.fromCallable(DBReader::getFeedList)
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
feeds -> {
@ -139,26 +143,33 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
StorageUtils.checkStorageAvailability(this);
final String feedUrl;
String feedUrl = null;
if (getIntent().hasExtra(ARG_FEEDURL)) {
feedUrl = getIntent().getStringExtra(ARG_FEEDURL);
} else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
if (actionBar != null) {
actionBar.setTitle(R.string.add_feed_label);
}
} else {
throw new IllegalArgumentException("Activity must be started with feedurl argument!");
}
Log.d(TAG, "Activity was started with url " + feedUrl);
setLoadingLayout();
if (savedInstanceState == null) {
startFeedDownload(feedUrl, null, null);
if (feedUrl == null) {
Log.e(TAG, "feedUrl is null.");
new AlertDialog.Builder(OnlineFeedViewActivity.this).
setNeutralButton(android.R.string.ok,
(dialog, which) -> finish()).
setTitle(R.string.error_label).
setMessage(R.string.null_value_podcast_error).create().show();
} else {
startFeedDownload(feedUrl, savedInstanceState.getString("username"), savedInstanceState.getString("password"));
Log.d(TAG, "Activity was started with url " + feedUrl);
setLoadingLayout();
if (savedInstanceState == null) {
startFeedDownload(feedUrl, null, null);
} else {
startFeedDownload(feedUrl, savedInstanceState.getString("username"), savedInstanceState.getString("password"));
}
}
}
@ -212,13 +223,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public void onDestroy() {
super.onDestroy();
if(updater != null) {
updater.unsubscribe();
updater.dispose();
}
if(download != null) {
download.unsubscribe();
download.dispose();
}
if(parser != null) {
parser.unsubscribe();
parser.dispose();
}
}
@ -273,18 +284,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
downloader.call();
return downloader.getResult();
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::checkDownloadResult,
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private void checkDownloadResult(DownloadStatus status) {
if (status == null) {
Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
finish();
return;
}
private void checkDownloadResult(@NonNull DownloadStatus status) {
if (status.isCancelled()) {
return;
}
@ -306,35 +312,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
private void parseFeed() {
if (feed == null || feed.getFile_url() == null && feed.isDownloaded()) {
if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) {
throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called");
}
Log.d(TAG, "Parsing feed");
parser = Observable.fromCallable(() -> {
FeedHandler handler = new FeedHandler();
try {
return handler.parseFeed(feed);
} 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());
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())
parser = Observable.fromCallable(this::doParseFeed)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if(result != null) {
.subscribe(optionalResult -> {
if(optionalResult.isPresent()) {
FeedHandlerResult result = optionalResult.get();
beforeShowFeedInformation(result.feed);
showFeedInformation(result.feed, result.alternateFeedUrls);
}
@ -342,9 +330,37 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
showErrorDialog(errorMsg);
Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
});
}
@NonNull
private Optional<FeedHandlerResult> doParseFeed() throws Exception {
FeedHandler handler = new FeedHandler();
try {
return Optional.of(handler.parseFeed(feed));
} catch (UnsupportedFeedtypeException e) {
Log.d(TAG, "Unsupported feed type detected");
if ("html".equalsIgnoreCase(e.getRootElement())) {
boolean dialogShown = showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
if (dialogShown) {
return Optional.empty();
} else {
Log.d(TAG, "Supplied feed is an HTML web page that has no references to any feed");
throw e;
}
} 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);
}
}
/**
* Called after the feed has been downloaded and parsed and before showFeedInformation is called.
* This method is executed on a background thread
@ -378,29 +394,30 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
EventDistributor.getInstance().register(listener);
ListView listView = (ListView) findViewById(R.id.listview);
ListView listView = findViewById(R.id.listview);
LayoutInflater inflater = LayoutInflater.from(this);
View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
listView.addHeaderView(header);
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
ImageView cover = (ImageView) header.findViewById(R.id.imgvCover);
TextView title = (TextView) header.findViewById(R.id.txtvTitle);
TextView author = (TextView) header.findViewById(R.id.txtvAuthor);
TextView description = (TextView) header.findViewById(R.id.txtvDescription);
Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls);
ImageView cover = header.findViewById(R.id.imgvCover);
TextView title = header.findViewById(R.id.txtvTitle);
TextView author = header.findViewById(R.id.txtvAuthor);
TextView description = header.findViewById(R.id.txtvDescription);
Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls);
subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
subscribeButton = 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())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.load(feed.getImageUrl())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(cover);
}
@ -527,21 +544,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
private void showFeedDiscoveryDialog(File feedFile, String baseUrl) {
/**
*
* @return true if a FeedDiscoveryDialog is shown, false otherwise (e.g., due to no feed found).
*/
private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) {
FeedDiscoverer fd = new FeedDiscoverer();
final Map<String, String> urlsMap;
try {
urlsMap = fd.findLinks(feedFile, baseUrl);
if (urlsMap == null || urlsMap.isEmpty()) {
return;
return false;
}
} catch (IOException e) {
e.printStackTrace();
return;
return false;
}
if (isPaused || isFinishing()) {
return;
return false;
}
final List<String> titles = new ArrayList<>();
@ -577,6 +598,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
dialog = ab.show();
});
return true;
}
private class FeedViewAuthenticationDialog extends AuthenticationDialog {

View File

@ -39,9 +39,9 @@ public class OpmlFeedChooserActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.opml_selection);
butConfirm = (Button) findViewById(R.id.butConfirm);
butCancel = (Button) findViewById(R.id.butCancel);
feedlist = (ListView) findViewById(R.id.feedlist);
butConfirm = findViewById(R.id.butConfirm);
butCancel = findViewById(R.id.butCancel);
feedlist = findViewById(R.id.feedlist);
feedlist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listAdapter = new ArrayAdapter<>(this,

View File

@ -36,16 +36,16 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.opml_import);
final TextView txtvHeaderExplanation1 = (TextView) findViewById(R.id.txtvHeadingExplanation1);
final TextView txtvExplanation1 = (TextView) findViewById(R.id.txtvExplanation1);
final TextView txtvHeaderExplanation2 = (TextView) findViewById(R.id.txtvHeadingExplanation2);
final TextView txtvExplanation2 = (TextView) findViewById(R.id.txtvExplanation2);
final TextView txtvHeaderExplanation3 = (TextView) findViewById(R.id.txtvHeadingExplanation3);
final TextView txtvHeaderExplanation1 = findViewById(R.id.txtvHeadingExplanation1);
final TextView txtvExplanation1 = findViewById(R.id.txtvExplanation1);
final TextView txtvHeaderExplanation2 = findViewById(R.id.txtvHeadingExplanation2);
final TextView txtvExplanation2 = findViewById(R.id.txtvExplanation2);
final TextView txtvHeaderExplanation3 = findViewById(R.id.txtvHeadingExplanation3);
Button butChooseFilesystem = (Button) findViewById(R.id.butChooseFileFromFilesystem);
Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem);
butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem());
Button butChooseExternal = (Button) findViewById(R.id.butChooseFileFromExternal);
Button butChooseExternal = findViewById(R.id.butChooseFileFromExternal);
butChooseExternal.setOnClickListener(v -> chooseFileFromExternal());
int nextOption = 1;

View File

@ -14,6 +14,8 @@ import de.danoeh.antennapod.core.export.opml.OpmlElement;
*/
public class OpmlImportHolder {
private OpmlImportHolder(){}
private static ArrayList<OpmlElement> readElements;
public static ArrayList<OpmlElement> getReadElements() {

View File

@ -12,11 +12,11 @@ import android.view.MenuItem;
import android.view.ViewGroup;
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 java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.PreferenceController;

View File

@ -1,23 +1,51 @@
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 io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.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.onComplete();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
});
}
}

View File

@ -20,10 +20,10 @@ import de.danoeh.antennapod.adapter.StatisticsListAdapter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'statistics' screen
@ -35,7 +35,7 @@ public class StatisticsActivity extends AppCompatActivity
private static final String PREF_NAME = "StatisticsActivityPrefs";
private static final String PREF_COUNT_ALL = "countAll";
private Subscription subscription;
private Disposable disposable;
private TextView totalTimeTextView;
private ListView feedStatisticsList;
private ProgressBar progressBar;
@ -53,9 +53,9 @@ public class StatisticsActivity extends AppCompatActivity
prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
totalTimeTextView = (TextView) findViewById(R.id.total_time);
feedStatisticsList = (ListView) findViewById(R.id.statistics_list);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
totalTimeTextView = findViewById(R.id.total_time);
feedStatisticsList = findViewById(R.id.statistics_list);
progressBar = findViewById(R.id.progressBar);
listAdapter = new StatisticsListAdapter(this);
listAdapter.setCountAll(countAll);
feedStatisticsList.setAdapter(listAdapter);
@ -119,21 +119,19 @@ public class StatisticsActivity extends AppCompatActivity
}
private void loadStatistics() {
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
subscription = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
totalTimeTextView.setText(Converter
.shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
listAdapter.update(result.feedTime);
progressBar.setVisibility(View.GONE);
totalTimeTextView.setVisibility(View.VISIBLE);
feedStatisticsList.setVisibility(View.VISIBLE);
}
totalTimeTextView.setText(Converter
.shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
listAdapter.update(result.feedTime);
progressBar.setVisibility(View.GONE);
totalTimeTextView.setVisibility(View.VISIBLE);
feedStatisticsList.setVisibility(View.VISIBLE);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}

View File

@ -42,7 +42,7 @@ public class StorageErrorActivity extends AppCompatActivity {
setContentView(R.layout.storage_error);
Button btnChooseDataFolder = (Button) findViewById(R.id.btnChooseDataFolder);
Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
btnChooseDataFolder.setOnClickListener(v -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {

View File

@ -8,6 +8,7 @@ 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;
@ -22,20 +23,19 @@ import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
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.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.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.view.AspectRatioVideoView;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Activity for playing video files.
*/
@ -47,6 +47,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
*/
private boolean videoControlsShowing = true;
private boolean videoSurfaceCreated = false;
private boolean playbackStoppedUponExitVideo = false;
private boolean destroyingDueToReload = false;
private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
@ -77,18 +78,9 @@ 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);
new PlaybackServiceStarter(this, media)
.startWhenPrepared(true)
.shouldStream(false)
.prepareImmediately(true)
.start();
playbackStoppedUponExitVideo = false;
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())) {
@ -101,12 +93,32 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onStop() {
stopPlaybackIfUserPreferencesSpecified(); // MUST be called before super.onStop(), while it still has member variable controller
super.onStop();
if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
videoControlsHider.stop();
}
}
void stopPlaybackIfUserPreferencesSpecified() {
// to avoid the method being called twice during leaving Videoplayer
// , which will double-pause the media
// (it is usually first called by surfaceHolderCallback.surfaceDestroyed(),
// then VideoplayerActivity.onStop() , but sometimes VideoplayerActivity.onStop()
// will first be invoked.)
if (playbackStoppedUponExitVideo) {
return;
}
playbackStoppedUponExitVideo = true;
if (controller != null && !destroyingDueToReload
&& UserPreferences.getVideoBackgroundBehavior()
!= UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
Log.v(TAG, "stop video playback per UserPreference");
controller.notifyVideoSurfaceAbandoned();
}
}
@Override
public void onUserLeaveHint () {
if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior()
@ -153,11 +165,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
super.setupGUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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);
controls = findViewById(R.id.controls);
videoOverlay = findViewById(R.id.overlay);
videoview = findViewById(R.id.videoview);
videoframe = findViewById(R.id.videoframe);
progressIndicator = findViewById(R.id.progressIndicator);
videoview.getHolder().addCallback(surfaceHolderCallback);
videoframe.setOnTouchListener(onVideoviewTouched);
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
@ -285,13 +297,12 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Videosurface was destroyed");
Log.d(TAG, "Videosurface was destroyed." );
Log.v(TAG, " hasController=" + (controller != null)
+ " , destroyingDueToReload=" + destroyingDueToReload
+ " , videoBackgroundBehavior=" + UserPreferences.getVideoBackgroundBehavior());
videoSurfaceCreated = false;
if (controller != null && !destroyingDueToReload
&& UserPreferences.getVideoBackgroundBehavior()
!= UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
controller.notifyVideoSurfaceAbandoned();
}
stopPlaybackIfUserPreferencesSpecified();
}
};

View File

@ -45,8 +45,6 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
public class GpodnetAuthenticationActivity extends AppCompatActivity {
private static final String TAG = "GpodnetAuthActivity";
private static final String CURRENT_STEP = "current_step";
private ViewFlipper viewFlipper;
private static final int STEP_DEFAULT = -1;
@ -72,7 +70,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
setContentView(R.layout.gpodnetauth_activity);
service = new GpodnetService();
viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper);
viewFlipper = findViewById(R.id.viewflipper);
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
views = new View[]{
@ -109,11 +107,11 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
}
private void setupLoginView(View view) {
final EditText username = (EditText) view.findViewById(R.id.etxtUsername);
final EditText password = (EditText) view.findViewById(R.id.etxtPassword);
final Button login = (Button) view.findViewById(R.id.butLogin);
final TextView txtvError = (TextView) view.findViewById(R.id.txtvError);
final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progBarLogin);
final EditText username = view.findViewById(R.id.etxtUsername);
final EditText password = view.findViewById(R.id.etxtPassword);
final Button login = view.findViewById(R.id.butLogin);
final TextView txtvError = view.findViewById(R.id.txtvError);
final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
password.setOnEditorActionListener((v, actionID, event) ->
actionID == EditorInfo.IME_ACTION_GO && login.performClick());
@ -177,13 +175,13 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
}
private void setupDeviceView(View view) {
final EditText deviceID = (EditText) view.findViewById(R.id.etxtDeviceID);
final EditText caption = (EditText) view.findViewById(R.id.etxtCaption);
final Button createNewDevice = (Button) view.findViewById(R.id.butCreateNewDevice);
final Button chooseDevice = (Button) view.findViewById(R.id.butChooseExistingDevice);
final TextView txtvError = (TextView) view.findViewById(R.id.txtvError);
final ProgressBar progBarCreateDevice = (ProgressBar) view.findViewById(R.id.progbarCreateDevice);
final Spinner spinnerDevices = (Spinner) view.findViewById(R.id.spinnerChooseDevice);
final EditText deviceID = view.findViewById(R.id.etxtDeviceID);
final EditText caption = view.findViewById(R.id.etxtCaption);
final Button createNewDevice = view.findViewById(R.id.butCreateNewDevice);
final Button chooseDevice = view.findViewById(R.id.butChooseExistingDevice);
final TextView txtvError = view.findViewById(R.id.txtvError);
final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
final Spinner spinnerDevices = view.findViewById(R.id.spinnerChooseDevice);
// load device list
@ -348,8 +346,8 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
}
private void setupFinishView(View view) {
final Button sync = (Button) view.findViewById(R.id.butSyncNow);
final Button back = (Button) view.findViewById(R.id.butGoMainscreen);
final Button sync = view.findViewById(R.id.butSyncNow);
final Button back = view.findViewById(R.id.butGoMainscreen);
sync.setOnClickListener(v -> {
GpodnetSyncService.sendSyncIntent(GpodnetAuthenticationActivity.this);

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
@ -19,7 +20,6 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify;
import java.lang.ref.WeakReference;
@ -28,13 +28,12 @@ import de.danoeh.antennapod.R;
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;
@ -51,7 +50,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final ActionButtonUtils actionButtonUtils;
private final boolean showOnlyNewEpisodes;
private int position = -1;
private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@ -67,11 +66,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);
}
@ -80,24 +75,24 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.new_episodes_listitem, parent, false);
Holder holder = new Holder(view);
holder.container = (FrameLayout) view.findViewById(R.id.container);
holder.content = (LinearLayout) view.findViewById(R.id.content);
holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder);
holder.title = (TextView) view.findViewById(R.id.txtvTitle);
holder.container = view.findViewById(R.id.container);
holder.content = view.findViewById(R.id.content);
holder.placeholder = view.findViewById(R.id.txtvPlaceholder);
holder.title = view.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.pubDate = (TextView) view
holder.pubDate = view
.findViewById(R.id.txtvPublished);
holder.statusUnread = view.findViewById(R.id.statusUnread);
holder.butSecondary = (ImageButton) view
holder.butSecondary = view
.findViewById(R.id.butSecondaryAction);
holder.queueStatus = (ImageView) view
holder.queueStatus = view
.findViewById(R.id.imgvInPlaylist);
holder.progress = (ProgressBar) view
holder.progress = view
.findViewById(R.id.pbar_progress);
holder.cover = (ImageView) view.findViewById(R.id.imgvCover);
holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration);
holder.cover = view.findViewById(R.id.imgvCover);
holder.txtvDuration = view.findViewById(R.id.txtvDuration);
holder.item = null;
holder.mainActivityRef = mainActivityRef;
// so we can grab this later
@ -111,7 +106,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
final FeedItem item = itemAccess.getItem(position);
if (item == null) return;
holder.itemView.setOnLongClickListener(v -> {
this.position = holder.getAdapterPosition();
this.selectedItem = item;
return false;
});
holder.item = item;
@ -196,12 +191,17 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(mainActivityRef.get())
.load(item.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageLocation(), holder.placeholder, holder.cover, mainActivityRef.get()));
new CoverLoader(mainActivityRef.get())
.withUri(item.getImageLocation())
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(holder.placeholder)
.withCoverView(holder.cover)
.load();
}
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
}
@Override
@ -215,16 +215,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return itemAccess.getCount();
}
public FeedItem getItem(int position) {
return itemAccess.getItem(position);
}
public int getPosition() {
int pos = position;
position = -1; // reset
return pos;
}
private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -299,6 +289,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

@ -19,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> {
@ -57,12 +57,12 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
holder.view = convertView;
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.title = convertView.findViewById(R.id.txtvTitle);
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
holder.duration = (TextView) convertView.findViewById(R.id.txtvDuration);
holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
holder.start = convertView.findViewById(R.id.txtvStart);
holder.link = convertView.findViewById(R.id.txtvLink);
holder.duration = convertView.findViewById(R.id.txtvDuration);
holder.butPlayChapter = convertView.findViewById(R.id.butPlayChapter);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@ -143,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

@ -0,0 +1,113 @@
package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.CustomViewTarget;
import java.lang.ref.WeakReference;
import com.bumptech.glide.request.transition.Transition;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
public class CoverLoader {
private String uri;
private String fallbackUri;
private TextView txtvPlaceholder;
private ImageView imgvCover;
private MainActivity activity;
private int errorResource = -1;
public CoverLoader(MainActivity activity) {
this.activity = activity;
}
public CoverLoader withUri(String uri) {
this.uri = uri;
return this;
}
public CoverLoader withFallbackUri(String uri) {
fallbackUri = uri;
return this;
}
public CoverLoader withCoverView(ImageView coverView) {
imgvCover = coverView;
return this;
}
public CoverLoader withError(int errorResource) {
this.errorResource = errorResource;
return this;
}
public CoverLoader withPlaceholderView(TextView placeholderView) {
txtvPlaceholder = placeholderView;
return this;
}
public void load() {
RequestOptions options = new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate();
if (errorResource != -1) {
options = options.error(errorResource);
}
RequestBuilder builder = Glide.with(activity)
.load(uri)
.apply(options);
if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
builder = builder.error(Glide.with(activity)
.load(fallbackUri)
.apply(options));
}
builder.into(new CoverTarget(txtvPlaceholder, imgvCover));
}
class CoverTarget extends CustomViewTarget<ImageView, Drawable> {
private final WeakReference<TextView> placeholder;
private final WeakReference<ImageView> cover;
public CoverTarget(TextView txtvPlaceholder, ImageView imgvCover) {
super(imgvCover);
placeholder = new WeakReference<>(txtvPlaceholder);
cover = new WeakReference<>(imgvCover);
}
@Override
public void onLoadFailed(Drawable errorDrawable) {
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
TextView txtvPlaceholder = placeholder.get();
if (txtvPlaceholder != null) {
txtvPlaceholder.setVisibility(View.INVISIBLE);
}
ImageView ivCover = cover.get();
ivCover.setImageDrawable(resource);
}
@Override
protected void onResourceCleared(@Nullable Drawable placeholder) {
ImageView ivCover = cover.get();
ivCover.setImageDrawable(placeholder);
}
}
}

View File

@ -1,57 +0,0 @@
package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
import java.lang.ref.WeakReference;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
class CoverTarget extends GlideDrawableImageViewTarget {
private final WeakReference<String> fallback;
private final WeakReference<TextView> placeholder;
private final WeakReference<ImageView> cover;
private final WeakReference<MainActivity> mainActivity;
public CoverTarget(String fallbackUri, TextView txtvPlaceholder, ImageView imgvCover, MainActivity activity) {
super(imgvCover);
fallback = new WeakReference<>(fallbackUri);
placeholder = new WeakReference<>(txtvPlaceholder);
cover = new WeakReference<>(imgvCover);
mainActivity = new WeakReference<>(activity);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
String fallbackUri = fallback.get();
TextView txtvPlaceholder = placeholder.get();
ImageView imgvCover = cover.get();
if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
MainActivity activity = mainActivity.get();
Glide.with(activity)
.load(fallbackUri)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(null, txtvPlaceholder, imgvCover, activity));
}
}
@Override
public void onResourceReady(GlideDrawable drawable, GlideAnimation<? super GlideDrawable> anim) {
super.onResourceReady(drawable, anim);
TextView txtvPlaceholder = placeholder.get();
if (txtvPlaceholder != null) {
txtvPlaceholder.setVisibility(View.INVISIBLE);
}
}
}

View File

@ -1,12 +1,10 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import org.apache.commons.lang3.Validate;
import de.danoeh.antennapod.R;
@ -20,15 +18,17 @@ 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.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
/**
* Default implementation of an ActionButtonCallback
*/
public class DefaultActionButtonCallback implements ActionButtonCallback {
private static final String TAG = "DefaultActionButtonCallback";
private static final String TAG = "DefaultActionBtnCb";
private final Context context;
@ -82,17 +82,13 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
} else { // media is downloaded
if (media.isCurrentlyPlaying()) {
new PlaybackServiceStarter(context, media)
.startWhenPrepared(true)
.shouldStream(false)
.start();
context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
} else if (media.isCurrentlyPaused()) {
new PlaybackServiceStarter(context, media)
new PlaybackServiceStarter(context, media) // need to start the service in case it's been stopped by system.
.startWhenPrepared(true)
.shouldStream(false)
.start();
context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE));
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;
@ -50,15 +49,15 @@ public class DownloadLogAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.icon = (IconTextView) convertView.findViewById(R.id.txtvIcon);
holder.retry = (IconButton) convertView.findViewById(R.id.btnRetry);
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.icon = convertView.findViewById(R.id.txtvIcon);
holder.retry = convertView.findViewById(R.id.btnRetry);
holder.date = convertView.findViewById(R.id.txtvDate);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
holder.reason = (TextView) convertView.findViewById(R.id.txtvReason);
holder.type = convertView.findViewById(R.id.txtvType);
holder.reason = convertView.findViewById(R.id.txtvReason);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@ -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

@ -13,9 +13,11 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
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;
@ -60,16 +62,16 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
parent, false);
holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.imageView = convertView.findViewById(R.id.imgvImage);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize);
holder.queueStatus = (ImageView) convertView.findViewById(R.id.imgvInPlaylist);
holder.pubDate = (TextView) convertView
holder.txtvSize = convertView.findViewById(R.id.txtvSize);
holder.queueStatus = convertView.findViewById(R.id.imgvInPlaylist);
holder.pubDate = convertView
.findViewById(R.id.txtvPublished);
holder.butSecondary = (ImageButton) convertView
holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
convertView.setTag(holder);
} else {
@ -78,11 +80,12 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
Glide.with(context)
.load(item.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.imageView);
if(item.isPlayed()) {
@ -98,7 +101,7 @@ 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 {

View File

@ -59,14 +59,14 @@ public class DownloadlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.downloaded = (TextView) convertView
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.downloaded = convertView
.findViewById(R.id.txtvDownloaded);
holder.percent = (TextView) convertView
holder.percent = convertView
.findViewById(R.id.txtvPercent);
holder.progbar = (ProgressBar) convertView
holder.progbar = convertView
.findViewById(R.id.progProgress);
holder.butSecondary = (ImageButton) convertView
holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
convertView.setTag(holder);
@ -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

@ -21,7 +21,6 @@ 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;
@ -60,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);
}
@ -95,24 +90,24 @@ public class FeedItemlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
holder.container = (LinearLayout) convertView
holder.container = convertView
.findViewById(R.id.container);
holder.title = (TextView) convertView.findViewById(R.id.txtvItemname);
holder.title = convertView.findViewById(R.id.txtvItemname);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.lenSize = (TextView) convertView
holder.lenSize = convertView
.findViewById(R.id.txtvLenSize);
holder.butAction = (ImageButton) convertView
holder.butAction = convertView
.findViewById(R.id.butSecondaryAction);
holder.published = (TextView) convertView
holder.published = convertView
.findViewById(R.id.txtvPublished);
holder.inPlaylist = (ImageView) convertView
holder.inPlaylist = convertView
.findViewById(R.id.imgvInPlaylist);
holder.type = (ImageView) convertView.findViewById(R.id.imgvType);
holder.type = convertView.findViewById(R.id.imgvType);
holder.statusUnread = convertView
.findViewById(R.id.statusUnread);
holder.episodeProgress = (ProgressBar) convertView
holder.episodeProgress = convertView
.findViewById(R.id.pbar_episode_progress);
convertView.setTag(holder);

View File

@ -34,9 +34,9 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.pubDate = convertView.findViewById(R.id.txtvPubDate);
holder.description = convertView.findViewById(R.id.txtvDescription);
convertView.setTag(holder);
} else {

View File

@ -8,6 +8,7 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -17,6 +18,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
@ -212,11 +214,18 @@ public class NavListAdapter extends BaseAdapter
v = getFeedView(position, convertView, parent);
}
if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) {
TextView txtvTitle = (TextView) v.findViewById(R.id.txtvTitle);
TextView txtvTitle = v.findViewById(R.id.txtvTitle);
TypedValue typedValue = new TypedValue();
if (position == itemAccess.getSelectedItemIndex()) {
txtvTitle.setTypeface(null, Typeface.BOLD);
v.getContext().getTheme().resolveAttribute(de.danoeh.antennapod.core.R.attr.drawer_activated_color, typedValue, true);
v.setBackgroundResource(typedValue.resourceId);
} else {
txtvTitle.setTypeface(null, Typeface.NORMAL);
v.getContext().getTheme().resolveAttribute(de.danoeh.antennapod.core.R.attr.nav_drawer_background, typedValue, true);
v.setBackgroundResource(typedValue.resourceId);
}
}
return v;
@ -235,9 +244,9 @@ public class NavListAdapter extends BaseAdapter
convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
holder.image = convertView.findViewById(R.id.imgvCover);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.count = convertView.findViewById(R.id.txtvCount);
convertView.setTag(holder);
} else {
holder = (NavHolder) convertView.getTag();
@ -325,10 +334,10 @@ public class NavListAdapter extends BaseAdapter
convertView = inflater.inflate(R.layout.nav_feedlistitem, parent, false);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.failure = (IconTextView) convertView.findViewById(R.id.itxtvFailure);
holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
holder.image = convertView.findViewById(R.id.imgvCover);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.failure = convertView.findViewById(R.id.itxtvFailure);
holder.count = convertView.findViewById(R.id.txtvCount);
convertView.setTag(holder);
} else {
holder = (FeedHolder) convertView.getTag();
@ -336,11 +345,12 @@ public class NavListAdapter extends BaseAdapter
Glide.with(context)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.image);
holder.title.setText(feed.getTitle());

View File

@ -23,6 +23,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import org.apache.commons.lang3.ArrayUtils;
@ -40,6 +41,7 @@ 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;
@ -75,11 +77,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);
}
@ -137,19 +135,19 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
public ViewHolder(View v) {
super(v);
container = (FrameLayout) v.findViewById(R.id.container);
dragHandle = (ImageView) v.findViewById(R.id.drag_handle);
placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder);
cover = (ImageView) v.findViewById(R.id.imgvCover);
title = (TextView) v.findViewById(R.id.txtvTitle);
container = v.findViewById(R.id.container);
dragHandle = v.findViewById(R.id.drag_handle);
placeholder = v.findViewById(R.id.txtvPlaceholder);
cover = v.findViewById(R.id.imgvCover);
title = v.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
pubDate = (TextView) v.findViewById(R.id.txtvPubDate);
progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft);
progressRight = (TextView) v.findViewById(R.id.txtvProgressRight);
butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
pubDate = v.findViewById(R.id.txtvPubDate);
progressLeft = v.findViewById(R.id.txtvProgressLeft);
progressRight = v.findViewById(R.id.txtvProgressRight);
butSecondary = v.findViewById(R.id.butSecondaryAction);
progressBar = v.findViewById(R.id.progressBar);
v.setTag(this);
v.setOnClickListener(this);
v.setOnCreateContextMenuListener(this);
@ -294,12 +292,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setTag(item);
butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(mainActivity.get())
.load(item.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageLocation(), placeholder, cover, mainActivity.get()));
new CoverLoader(mainActivity.get())
.withUri(item.getImageLocation())
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
}

View File

@ -12,6 +12,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
@ -61,13 +62,13 @@ public class SearchlistAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.cover = (ImageView) convertView
holder.cover = convertView
.findViewById(R.id.imgvFeedimage);
holder.subtitle = (TextView) convertView
holder.subtitle = convertView
.findViewById(R.id.txtvSubtitle);
convertView.setTag(holder);
@ -81,11 +82,12 @@ public class SearchlistAdapter extends BaseAdapter {
Glide.with(context)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
@ -100,11 +102,12 @@ public class SearchlistAdapter extends BaseAdapter {
Glide.with(context)
.load(item.getFeed().getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.cover);
}

View File

@ -13,6 +13,7 @@ import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@ -62,9 +63,9 @@ public class StatisticsListAdapter extends BaseAdapter {
convertView = inflater.inflate(R.layout.statistics_listitem, parent, false);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.time = (TextView) convertView.findViewById(R.id.txtvTime);
holder.image = convertView.findViewById(R.id.imgvCover);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.time = convertView.findViewById(R.id.txtvTime);
convertView.setTag(holder);
} else {
holder = (StatisticsHolder) convertView.getTag();
@ -72,11 +73,12 @@ public class StatisticsListAdapter extends BaseAdapter {
Glide.with(context)
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.image);
holder.title.setText(feed.getTitle());

View File

@ -14,6 +14,7 @@ import com.bumptech.glide.Glide;
import java.lang.ref.WeakReference;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
@ -89,9 +90,9 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
LayoutInflater layoutInflater =
(LayoutInflater) mainActivityRef.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.subscription_item, parent, false);
holder.feedTitle = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.imageView = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.count = (TriangleLabelView) convertView.findViewById(R.id.triangleCountView);
holder.feedTitle = convertView.findViewById(R.id.txtvTitle);
holder.imageView = convertView.findViewById(R.id.imgvCover);
holder.count = convertView.findViewById(R.id.triangleCountView);
convertView.setTag(holder);
@ -108,7 +109,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder.count.setVisibility(View.INVISIBLE);
// when this holder is reused, we could else end up with a cover image
Glide.clear(holder.imageView);
Glide.with(mainActivityRef.get()).clear(holder.imageView);
return convertView;
}
@ -125,13 +126,13 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
} else {
holder.count.setVisibility(View.GONE);
}
Glide.with(mainActivityRef.get())
.load(feed.getImageLocation())
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(null, holder.feedTitle, holder.imageView, mainActivityRef.get()));
new CoverLoader(mainActivityRef.get())
.withUri(feed.getImageLocation())
.withPlaceholderView(holder.feedTitle)
.withCoverView(holder.imageView)
.withError(R.color.light_gray)
.load();
return convertView;
}

View File

@ -10,6 +10,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@ -40,10 +41,10 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, parent, false);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.subscribers = (TextView) convertView.findViewById(R.id.txtvSubscribers);
holder.url = (TextView) convertView.findViewById(R.id.txtvUrl);
holder.image = convertView.findViewById(R.id.imgvCover);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.subscribers = convertView.findViewById(R.id.txtvSubscribers);
holder.url = convertView.findViewById(R.id.txtvUrl);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@ -52,11 +53,12 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
if (StringUtils.isNotBlank(podcast.getLogoUrl())) {
Glide.with(convertView.getContext())
.load(podcast.getLogoUrl())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.image);
}

View File

@ -34,8 +34,8 @@ public class TagListAdapter extends ArrayAdapter<GpodnetTag> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.gpodnet_tag_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.usage = (TextView) convertView.findViewById(R.id.txtvUsage);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.usage = convertView.findViewById(R.id.txtvUsage);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();

View File

@ -12,6 +12,8 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -80,10 +82,11 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
//Update the empty imageView with the image from the feed
Glide.with(context)
.load(podcast.imageUrl)
.placeholder(R.color.light_gray)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.fitCenter()
.dontAnimate())
.into(viewHolder.coverView);
//Feed the grid view
@ -132,7 +135,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
}
public static Podcast fromSearch(SearchHit searchHit) {
return new Podcast(searchHit.getTitle(), searchHit.getImageUrl(), searchHit.getXmlUrl());
return new Podcast(searchHit.getTitle(), searchHit.getThumbImageURL(), searchHit.getXmlUrl());
}
/**
@ -182,9 +185,9 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @param view GridView cell
*/
PodcastViewHolder(View view){
coverView = (ImageView) view.findViewById(R.id.imgvCover);
titleView = (TextView) view.findViewById(R.id.txtvTitle);
urlView = (TextView) view.findViewById(R.id.txtvUrl);
coverView = view.findViewById(R.id.imgvCover);
titleView = view.findViewById(R.id.txtvTitle);
urlView = view.findViewById(R.id.txtvUrl);
}
}
}

View File

@ -12,7 +12,7 @@ import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.LangUtils;
import rx.Observable;
import io.reactivex.Observable;
/**
* Writes an OPML file into the export directory in the background.
@ -23,15 +23,15 @@ public class ExportWorker {
private static final String TAG = "ExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
private final ExportWriter exportWriter;
private final File output;
private final @NonNull ExportWriter exportWriter;
private final @NonNull File output;
public ExportWorker(ExportWriter exportWriter) {
public ExportWorker(@NonNull ExportWriter exportWriter) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
}
private ExportWorker(ExportWriter exportWriter, @NonNull File output) {
private ExportWorker(@NonNull ExportWriter exportWriter, @NonNull File output) {
this.exportWriter = exportWriter;
this.output = output;
}
@ -57,7 +57,7 @@ public class ExportWorker {
subscriber.onError(e);
}
}
subscriber.onCompleted();
subscriber.onComplete();
}
});
}

View File

@ -8,6 +8,8 @@ import de.danoeh.antennapod.core.ClientConfig;
*/
class ClientConfigurator {
private ClientConfigurator(){}
static {
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();

View File

@ -2,7 +2,6 @@ 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;

View File

@ -35,11 +35,11 @@ public abstract class AuthenticationDialog extends Dialog {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.authentication_dialog);
final EditText etxtUsername = (EditText) findViewById(R.id.etxtUsername);
final EditText etxtPassword = (EditText) findViewById(R.id.etxtPassword);
final CheckBox saveUsernamePassword = (CheckBox) findViewById(R.id.chkSaveUsernamePassword);
final Button butConfirm = (Button) findViewById(R.id.butConfirm);
final Button butCancel = (Button) findViewById(R.id.butCancel);
final EditText etxtUsername = findViewById(R.id.etxtUsername);
final EditText etxtPassword = findViewById(R.id.etxtPassword);
final CheckBox saveUsernamePassword = findViewById(R.id.chkSaveUsernamePassword);
final Button butConfirm = findViewById(R.id.butConfirm);
final Button butCancel = findViewById(R.id.butCancel);
if (titleRes != 0) {
setTitle(titleRes);

View File

@ -29,9 +29,9 @@ public class AutoFlattrPreferenceDialog {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
@SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null);
final CheckBox chkAutoFlattr = (CheckBox) view.findViewById(R.id.chkAutoFlattr);
final SeekBar skbPercent = (SeekBar) view.findViewById(R.id.skbPercent);
final TextView txtvStatus = (TextView) view.findViewById(R.id.txtvStatus);
final CheckBox chkAutoFlattr = view.findViewById(R.id.chkAutoFlattr);
final SeekBar skbPercent = view.findViewById(R.id.skbPercent);
final TextView txtvStatus = view.findViewById(R.id.txtvStatus);
chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr());
skbPercent.setEnabled(chkAutoFlattr.isChecked());

View File

@ -1,11 +1,20 @@
package de.danoeh.antennapod.dialog;
import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.PluralsRes;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.util.ArrayMap;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -13,11 +22,12 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -34,22 +44,42 @@ public class EpisodesApplyActionFragment extends Fragment {
public static final String TAG = "EpisodeActionFragment";
private static final int ACTION_QUEUE = 1;
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;
private static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
| ACTION_DOWNLOAD | ACTION_REMOVE;
public static final int ACTION_ADD_TO_QUEUE = 1;
private static final int ACTION_REMOVE_FROM_QUEUE = 2;
private static final int ACTION_MARK_PLAYED = 4;
private static final int ACTION_MARK_UNPLAYED = 8;
private static final int ACTION_DOWNLOAD = 16;
public static final int ACTION_DELETE = 32;
private static final int ACTION_ALL = ACTION_ADD_TO_QUEUE | ACTION_REMOVE_FROM_QUEUE
| ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED | ACTION_DOWNLOAD | ACTION_DELETE;
/**
* Specify an action (defined by #flag) 's UI bindings.
*
* Includes: the menu / action item and the actual logic
*/
private class ActionBinding {
int flag;
@IdRes
final int actionItemId;
@NonNull
final Runnable action;
ActionBinding(int flag, @IdRes int actionItemId, @NonNull Runnable action) {
this.flag = flag;
this.actionItemId = actionItemId;
this.action = action;
}
}
private final List<? extends ActionBinding> actionBindings;
private ListView mListView;
private ArrayAdapter<String> mAdapter;
private Button btnAddToQueue;
private Button btnMarkAsPlayed;
private Button btnMarkAsUnplayed;
private Button btnDownload;
private Button btnDelete;
private SpeedDialView mSpeedDialView;
@NonNull
private CharSequence actionBarTitleOriginal = "";
private final Map<Long,FeedItem> idMap = new ArrayMap<>();
private final List<FeedItem> episodes = new ArrayList<>();
@ -59,6 +89,23 @@ public class EpisodesApplyActionFragment extends Fragment {
private MenuItem mSelectToggle;
public EpisodesApplyActionFragment() {
actionBindings = Arrays.asList(
new ActionBinding(ACTION_ADD_TO_QUEUE,
R.id.add_to_queue_batch, this::queueChecked),
new ActionBinding(ACTION_REMOVE_FROM_QUEUE,
R.id.remove_from_queue_batch, this::removeFromQueueChecked),
new ActionBinding(ACTION_MARK_PLAYED,
R.id.mark_read_batch, this::markedCheckedPlayed),
new ActionBinding(ACTION_MARK_UNPLAYED,
R.id.mark_unread_batch, this::markedCheckedUnplayed),
new ActionBinding(ACTION_DOWNLOAD,
R.id.download_batch, this::downloadChecked),
new ActionBinding(ACTION_DELETE,
R.id.delete_batch, this::deleteChecked)
);
}
public static EpisodesApplyActionFragment newInstance(List<FeedItem> items) {
return newInstance(items, ACTION_ALL);
}
@ -76,6 +123,7 @@ public class EpisodesApplyActionFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
@ -84,7 +132,7 @@ public class EpisodesApplyActionFragment extends Fragment {
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false);
mListView = (ListView) view.findViewById(android.R.id.list);
mListView = view.findViewById(android.R.id.list);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setOnItemClickListener((ListView, view1, position, rowId) -> {
long id = episodes.get(position).getId();
@ -95,62 +143,85 @@ public class EpisodesApplyActionFragment extends Fragment {
}
refreshCheckboxes();
});
mListView.setOnItemLongClickListener((adapterView, view12, position, id) -> {
new AlertDialog.Builder(getActivity())
.setItems(R.array.batch_long_press_options, (dialogInterface, item) -> {
int direction;
if (item == 0) {
direction = -1;
} else {
direction = 1;
}
int currentPosition = position + direction;
while (currentPosition >= 0 && currentPosition < episodes.size()) {
long id1 = episodes.get(currentPosition).getId();
if (!checkedIds.contains(id1)) {
checkedIds.add(id1);
}
currentPosition += direction;
}
refreshCheckboxes();
}).show();
return true;
});
for(FeedItem episode : episodes) {
titles.add(episode.getTitle());
}
mAdapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_multiple_choice, titles);
R.layout.simple_list_item_multiple_choice_on_start, titles);
mListView.setAdapter(mAdapter);
checkAll();
int lastVisibleDiv = 0;
btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
if((actions & ACTION_QUEUE) != 0) {
btnAddToQueue.setOnClickListener(v -> queueChecked());
lastVisibleDiv = R.id.divider1;
} else {
btnAddToQueue.setVisibility(View.GONE);
view.findViewById(R.id.divider1).setVisibility(View.GONE);
}
btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
if((actions & ACTION_MARK_PLAYED) != 0) {
btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
lastVisibleDiv = R.id.divider2;
} else {
btnMarkAsPlayed.setVisibility(View.GONE);
view.findViewById(R.id.divider2).setVisibility(View.GONE);
}
btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
if((actions & ACTION_MARK_UNPLAYED) != 0) {
btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
lastVisibleDiv = R.id.divider3;
} else {
btnMarkAsUnplayed.setVisibility(View.GONE);
view.findViewById(R.id.divider3).setVisibility(View.GONE);
}
btnDownload = (Button) view.findViewById(R.id.btnDownload);
if((actions & ACTION_DOWNLOAD) != 0) {
btnDownload.setOnClickListener(v -> downloadChecked());
lastVisibleDiv = R.id.divider4;
} else {
btnDownload.setVisibility(View.GONE);
view.findViewById(R.id.divider4).setVisibility(View.GONE);
}
btnDelete = (Button) view.findViewById(R.id.btnDelete);
if((actions & ACTION_REMOVE) != 0) {
btnDelete.setOnClickListener(v -> deleteChecked());
} else {
btnDelete.setVisibility(View.GONE);
if(lastVisibleDiv > 0) {
view.findViewById(lastVisibleDiv).setVisibility(View.GONE);
saveActionBarTitle(); // needed when we dynamically change the title based on selection
// Init action UI (via a FAB Speed Dial)
mSpeedDialView = view.findViewById(R.id.fabSD);
mSpeedDialView.inflate(R.menu.episodes_apply_action_speeddial);
// show only specified actions, and bind speed dial UIs to the actual logic
for (ActionBinding binding : actionBindings) {
if ((actions & binding.flag) == 0) {
mSpeedDialView.removeActionItemById(binding.actionItemId);
}
}
mSpeedDialView.setOnActionSelectedListener(actionItem -> {
ActionBinding selectedBinding = null;
for (ActionBinding binding : actionBindings) {
if (actionItem.getId() == binding.actionItemId) {
selectedBinding = binding;
break;
}
}
if (selectedBinding != null) {
selectedBinding.action.run();
} else {
Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + actionItem.getId());
}
return true;
});
showSpeedDialIfAnyChecked();
return view;
}
@Override
public void onStop() {
restoreActionBarTitle(); // it might have been changed to "N selected". Restore original.
super.onStop();
}
private void showSpeedDialIfAnyChecked() {
mSpeedDialView.setVisibility(checkedIds.size() > 0 ? View.VISIBLE : View.GONE);
}
private void hideSpeedDial() {
mSpeedDialView.setVisibility(View.GONE);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
@ -173,11 +244,9 @@ public class EpisodesApplyActionFragment extends Fragment {
int[] icon = new int[1];
if (checkedIds.size() == episodes.size()) {
icon[0] = R.attr.ic_check_box;
} else if (checkedIds.size() == 0) {
icon[0] = R.attr.ic_check_box_outline;
icon[0] = R.attr.ic_select_none;
} else {
icon[0] = R.attr.ic_indeterminate_check_box;
icon[0] = R.attr.ic_select_all;
}
TypedArray a = getActivity().obtainStyledAttributes(icon);
@ -189,7 +258,7 @@ public class EpisodesApplyActionFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int resId = 0;
@StringRes int resId = 0;
switch(item.getItemId()) {
case R.id.select_options:
return true;
@ -249,7 +318,8 @@ public class EpisodesApplyActionFragment extends Fragment {
return true;
}
if(resId != 0) {
Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
.show();
return true;
} else {
return false;
@ -387,21 +457,56 @@ public class EpisodesApplyActionFragment extends Fragment {
mListView.setItemChecked(i, checked);
}
ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity());
showSpeedDialIfAnyChecked();
updateActionBarTitle();
}
private void saveActionBarTitle() {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
CharSequence title = actionBar.getTitle();
if (title == null) {
title = "";
}
actionBarTitleOriginal = title;
}
}
private void restoreActionBarTitle() {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(actionBarTitleOriginal);
}
}
private void updateActionBarTitle() {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
CharSequence title = checkedIds.size() > 0 ?
getString(R.string.num_selected_label, checkedIds.size()) :
actionBarTitleOriginal;
actionBar.setTitle(title);
}
}
private void queueChecked() {
DBWriter.addQueueItem(getActivity(), true, checkedIds.toArray());
close();
close(R.plurals.added_to_queue_batch_label, checkedIds.size());
}
private void removeFromQueueChecked() {
DBWriter.removeQueueItem(getActivity(), true, checkedIds.toArray());
close(R.plurals.removed_from_queue_batch_label, checkedIds.size());
}
private void markedCheckedPlayed() {
DBWriter.markItemPlayed(FeedItem.PLAYED, checkedIds.toArray());
close();
close(R.plurals.marked_read_batch_label, checkedIds.size());
}
private void markedCheckedUnplayed() {
DBWriter.markItemPlayed(FeedItem.UNPLAYED, checkedIds.toArray());
close();
close(R.plurals.marked_unread_batch_label, checkedIds.size());
}
private void downloadChecked() {
@ -418,7 +523,7 @@ public class EpisodesApplyActionFragment extends Fragment {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
}
close();
close(R.plurals.downloading_batch_label, checkedIds.size());
}
private void deleteChecked() {
@ -428,10 +533,18 @@ public class EpisodesApplyActionFragment extends Fragment {
DBWriter.deleteFeedMediaOfItem(getActivity(), episode.getMedia().getId());
}
}
close();
close(R.plurals.deleted_episode_batch_label, checkedIds.size());
}
private void close() {
private void close(@PluralsRes int msgId, int numItems) {
if (numItems > 0) {
Snackbar.make(getActivity().findViewById(R.id.content),
getResources().getQuantityString(msgId, numItems, numItems),
Snackbar.LENGTH_LONG
)
.setAction(android.R.string.ok, v -> {})
.show();
}
getActivity().getSupportFragmentManager().popBackStack();
}

View File

@ -17,6 +17,9 @@ import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
* Creates a dialog that lets the user change the hostname for the gpodder.net service.
*/
public class GpodnetSetHostnameDialog {
private GpodnetSetHostnameDialog(){}
private static final String TAG = "GpodnetSetHostnameDialog";
public static AlertDialog createDialog(final Context context) {

View File

@ -29,15 +29,15 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class ProxyDialog {
@ -55,7 +55,7 @@ public class ProxyDialog {
private boolean testSuccessful = false;
private TextView txtvMessage;
private Subscription subscription;
private Disposable disposable;
public ProxyDialog(Context context) {
this.context = context;
@ -102,7 +102,7 @@ public class ProxyDialog {
.autoDismiss(false)
.build();
View view = dialog.getCustomView();
spType = (Spinner) view.findViewById(R.id.spType);
spType = view.findViewById(R.id.spType);
String[] types = { Proxy.Type.DIRECT.name(), Proxy.Type.HTTP.name() };
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, types);
@ -110,22 +110,22 @@ public class ProxyDialog {
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
etHost = (EditText) view.findViewById(R.id.etHost);
etHost = view.findViewById(R.id.etHost);
if(!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
etPort = (EditText) view.findViewById(R.id.etPort);
etPort = view.findViewById(R.id.etPort);
if(proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
etUsername = (EditText) view.findViewById(R.id.etUsername);
etUsername = view.findViewById(R.id.etUsername);
if(!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
etPassword = (EditText) view.findViewById(R.id.etPassword);
etPassword = view.findViewById(R.id.etPassword);
if(!TextUtils.isEmpty(proxyConfig.password)) {
etPassword.setText(proxyConfig.username);
}
@ -146,7 +146,7 @@ public class ProxyDialog {
enableSettings(false);
}
});
txtvMessage = (TextView) view.findViewById(R.id.txtvMessage);
txtvMessage = view.findViewById(R.id.txtvMessage);
checkValidity();
return dialog;
}
@ -229,8 +229,8 @@ public class ProxyDialog {
}
private void test() {
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
if(!checkValidity()) {
setTestRequired(true);
@ -243,7 +243,7 @@ public class ProxyDialog {
txtvMessage.setTextColor(textColorPrimary);
txtvMessage.setText("{fa-circle-o-notch spin} " + checking);
txtvMessage.setVisibility(View.VISIBLE);
subscription = Observable.create((Observable.OnSubscribe<Response>) subscriber -> {
disposable = Single.create((SingleOnSubscribe<Response>) emitter -> {
String type = (String) spType.getSelectedItem();
String host = etHost.getText().toString();
String port = etPort.getText().toString();
@ -275,13 +275,12 @@ public class ProxyDialog {
.build();
try {
Response response = client.newCall(request).execute();
subscriber.onNext(response);
emitter.onSuccess(response);
} catch(IOException e) {
subscriber.onError(e);
emitter.onError(e);
}
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {

View File

@ -17,6 +17,8 @@ import de.danoeh.antennapod.R;
public class RatingDialog {
private RatingDialog(){}
private static final String TAG = RatingDialog.class.getSimpleName();
private static final int AFTER_DAYS = 7;

View File

@ -61,11 +61,11 @@ public abstract class SleepTimerDialog {
dialog = builder.build();
View view = dialog.getView();
etxtTime = (EditText) view.findViewById(R.id.etxtTime);
spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit);
cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset);
cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate);
chAutoEnable = (CheckBox) view.findViewById(R.id.chAutoEnable);
etxtTime = view.findViewById(R.id.etxtTime);
spTimeUnit = view.findViewById(R.id.spTimeUnit);
cbShakeToReset = view.findViewById(R.id.cbShakeToReset);
cbVibrate = view.findViewById(R.id.cbVibrate);
chAutoEnable = view.findViewById(R.id.chAutoEnable);
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {

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

@ -32,18 +32,18 @@ public class AddFeedFragment extends Fragment {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.addfeed, container, false);
final EditText etxtFeedurl = (EditText) root.findViewById(R.id.etxtFeedurl);
final EditText etxtFeedurl = root.findViewById(R.id.etxtFeedurl);
Bundle args = getArguments();
if (args != null && args.getString(ARG_FEED_URL) != null) {
etxtFeedurl.setText(args.getString(ARG_FEED_URL));
}
Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes);
Button butBrowserGpoddernet = (Button) root.findViewById(R.id.butBrowseGpoddernet);
Button butSearchFyyd = (Button) root.findViewById(R.id.butSearchFyyd);
Button butOpmlImport = (Button) root.findViewById(R.id.butOpmlImport);
Button butConfirm = (Button) root.findViewById(R.id.butConfirm);
Button butSearchITunes = root.findViewById(R.id.butSearchItunes);
Button butBrowserGpoddernet = root.findViewById(R.id.butBrowseGpoddernet);
Button butSearchFyyd = root.findViewById(R.id.butSearchFyyd);
Button butOpmlImport = root.findViewById(R.id.butOpmlImport);
Button butConfirm = root.findViewById(R.id.butConfirm);
final MainActivity activity = (MainActivity) getActivity();
activity.getSupportActionBar().setTitle(R.string.add_feed_label);
@ -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,9 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
@ -18,6 +21,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
@ -36,22 +40,22 @@ 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;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Shows unread or recently published episodes
@ -72,6 +76,7 @@ public class AllEpisodesFragment extends Fragment {
RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
private View emptyView;
List<FeedItem> episodes;
private List<Downloader> downloaderList;
@ -82,7 +87,7 @@ public class AllEpisodesFragment extends Fragment {
private boolean isUpdatingFeeds;
boolean isMenuInvalidationAllowed = false;
Subscription subscription;
Disposable disposable;
private LinearLayoutManager layoutManager;
boolean showOnlyNewEpisodes() { return false; }
@ -123,8 +128,8 @@ public class AllEpisodesFragment extends Fragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}
@ -273,17 +278,23 @@ public class AllEpisodesFragment extends Fragment {
if(item.getItemId() == R.id.share_item) {
return true; // avoids that the position is reset when we need it in the submenu
}
int pos = listAdapter.getPosition();
if(pos < 0) {
return false;
}
FeedItem selectedItem = itemAccess.getItem(pos);
FeedItem selectedItem = listAdapter.getSelectedItem();
if (selectedItem == null) {
Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection");
Log.i(TAG, "Selected item was null, ignoring selection");
return super.onContextItemSelected(item);
}
// 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);
}
@ -301,7 +312,7 @@ public class AllEpisodesFragment extends Fragment {
View root = inflater.inflate(fragmentResource, container, false);
recyclerView = (RecyclerView) root.findViewById(android.R.id.list);
recyclerView = root.findViewById(android.R.id.list);
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
@ -311,7 +322,7 @@ public class AllEpisodesFragment extends Fragment {
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
progLoading = root.findViewById(R.id.progLoading);
if (!itemsLoaded) {
progLoading.setVisibility(View.VISIBLE);
@ -323,17 +334,31 @@ public class AllEpisodesFragment extends Fragment {
onFragmentLoaded();
}
emptyView = (View) root.findViewById(R.id.emptyView);
emptyView.setVisibility(View.GONE);
((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_all_episodes_head_label);
((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_all_episodes_label);
return root;
}
private void onFragmentLoaded() {
if (listAdapter == null) {
MainActivity mainActivity = (MainActivity) getActivity();
listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
listAdapter.setHasStableIds(true);
recyclerView.setAdapter(listAdapter);
if (episodes != null && episodes.size() > 0) {
if (listAdapter == null) {
MainActivity mainActivity = (MainActivity) getActivity();
listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
listAdapter.setHasStableIds(true);
recyclerView.setAdapter(listAdapter);
}
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
} else {
listAdapter = null;
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
}
listAdapter.notifyDataSetChanged();
restoreScrollPosition();
getActivity().supportInvalidateOptionsMenu();
@ -406,20 +431,26 @@ public class AllEpisodesFragment extends Fragment {
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if(episodes == null || listAdapter == null) {
if (episodes == null || listAdapter == null) {
return;
}
for(int i=0, size = event.items.size(); i < size; i++) {
FeedItem item = event.items.get(i);
for (FeedItem item : event.items) {
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
if(pos >= 0) {
if (pos >= 0) {
episodes.remove(pos);
episodes.add(pos, item);
listAdapter.notifyItemChanged(pos);
if (shouldUpdatedItemRemainInList(item)) {
episodes.add(pos, item);
listAdapter.notifyItemChanged(pos);
} else {
listAdapter.notifyItemRemoved(pos);
}
}
}
}
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
return true;
}
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
@ -454,31 +485,63 @@ public class AllEpisodesFragment extends Fragment {
}
void loadItems() {
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
if (viewsCreated && !itemsLoaded) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
subscription = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
recyclerView.setVisibility(View.VISIBLE);
progLoading.setVisibility(View.GONE);
if (data != null) {
episodes = data;
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
}
episodes = data;
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@NonNull
List<FeedItem> loadData() {
return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
void markItemAsSeenWithUndo(FeedItem item) {
if (item == null) {
return;
}
Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")");
if (disposable != null) {
disposable.dispose();
}
// 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

@ -1,8 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
@ -12,9 +10,6 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.List;
import de.danoeh.antennapod.R;
@ -22,15 +17,15 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
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.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays all running downloads and provides a button to delete them
@ -48,7 +43,7 @@ public class CompletedDownloadsFragment extends ListFragment {
private boolean viewCreated = false;
private Subscription subscription;
private Disposable disposable;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -67,16 +62,16 @@ public class CompletedDownloadsFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}
@Override
public void onDetach() {
super.onDetach();
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}
@ -85,8 +80,8 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onDestroyView();
listAdapter = null;
viewCreated = false;
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
}
@ -112,6 +107,11 @@ public class CompletedDownloadsFragment extends ListFragment {
if (items != null && getActivity() != null) {
onFragmentLoaded();
}
EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
emptyView.setTitle(R.string.no_comp_downloads_head_label);
emptyView.setMessage(R.string.no_comp_downloads_label);
emptyView.attachToListView(getListView());
}
@Override
@ -149,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_DELETE | EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE);
((MainActivity) getActivity()).loadChildFragment(fragment);
return true;
default:
@ -188,21 +188,19 @@ public class CompletedDownloadsFragment extends ListFragment {
};
private void loadItems() {
if(subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
if (items == null && viewCreated) {
setListShown(false);
}
subscription = Observable.fromCallable(DBReader::getDownloadedItems)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(DBReader::getDownloadedItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
items = result;
if (viewCreated && getActivity() != null) {
onFragmentLoaded();
}
items = result;
if (viewCreated && getActivity() != null) {
onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}

View File

@ -11,6 +11,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@ -22,7 +23,6 @@ import de.danoeh.antennapod.core.util.playback.Playable;
public class CoverFragment extends Fragment implements MediaplayerInfoContentFragment {
private static final String TAG = "CoverFragment";
private static final String ARG_PLAYABLE = "arg.playable";
private Playable media;
@ -48,10 +48,11 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
root = inflater.inflate(R.layout.cover_fragment, container, false);
txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = root.findViewById(R.id.imgvCover);
return root;
}
@ -61,9 +62,10 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
.load(media.getImageLocation())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
.fitCenter()
.apply(new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
.fitCenter())
.into(imgvCover);
} else {
Log.w(TAG, "loadMediaInfo was called while media was null");

View File

@ -12,21 +12,23 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
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.Feed;
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;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Shows the download log
@ -41,7 +43,7 @@ public class DownloadLogFragment extends ListFragment {
private boolean viewsCreated = false;
private boolean itemsLoaded = false;
private Subscription subscription;
private Disposable disposable;
@Override
public void onStart() {
@ -55,8 +57,8 @@ public class DownloadLogFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
}
@ -74,6 +76,12 @@ public class DownloadLogFragment extends ListFragment {
if (itemsLoaded) {
onFragmentLoaded();
}
EmptyViewHandler emptyView = new EmptyViewHandler(getActivity());
emptyView.setTitle(R.string.no_log_downloads_head_label);
emptyView.setMessage(R.string.no_log_downloads_label);
emptyView.attachToListView(getListView());
}
private void onFragmentLoaded() {
@ -93,10 +101,18 @@ public class DownloadLogFragment extends ListFragment {
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.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
if (media != null) {
url = media.getDownload_url();
}
} else if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
Feed feed = DBReader.getFeed(status.getFeedfileId());
if (feed != null) {
url = feed.getDownload_url();
}
}
if (!status.isSuccessful()) {
message = status.getReasonDetailed();
}
@ -178,11 +194,11 @@ public class DownloadLogFragment extends ListFragment {
}
private void loadItems() {
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
subscription = Observable.fromCallable(DBReader::getDownloadLog)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(DBReader::getDownloadLog)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {

View File

@ -38,12 +38,12 @@ public class DownloadsFragment extends Fragment {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
viewPager = (ViewPager)root.findViewById(R.id.viewpager);
viewPager = root.findViewById(R.id.viewpager);
DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources());
viewPager.setAdapter(pagerAdapter);
// Give the TabLayout the ViewPager
tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs);
tabLayout = root.findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
return root;

View File

@ -46,11 +46,11 @@ public class EpisodesFragment extends Fragment {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.episodes_label);
View rootView = inflater.inflate(R.layout.pager_fragment, container, false);
viewPager = (ViewPager)rootView.findViewById(R.id.viewpager);
viewPager = rootView.findViewById(R.id.viewpager);
viewPager.setAdapter(new EpisodesPagerAdapter(getChildFragmentManager(), getResources()));
// Give the TabLayout the ViewPager
tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs);
tabLayout = rootView.findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
return rootView;

View File

@ -16,13 +16,17 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
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 io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
@ -38,6 +42,7 @@ public class ExternalPlayerFragment extends Fragment {
private TextView mFeedName;
private ProgressBar mProgressBar;
private PlaybackController controller;
private Disposable disposable;
public ExternalPlayerFragment() {
super();
@ -48,12 +53,12 @@ public class ExternalPlayerFragment extends Fragment {
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.external_player_fragment,
container, false);
fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
butPlay = (ImageButton) root.findViewById(R.id.butPlay);
mFeedName = (TextView) root.findViewById(R.id.txtvAuthor);
mProgressBar = (ProgressBar) root.findViewById(R.id.episodeProgress);
fragmentLayout = root.findViewById(R.id.fragmentLayout);
imgvCover = root.findViewById(R.id.imgvCover);
txtvTitle = root.findViewById(R.id.txtvTitle);
butPlay = root.findViewById(R.id.butPlay);
mFeedName = root.findViewById(R.id.txtvAuthor);
mProgressBar = root.findViewById(R.id.episodeProgress);
fragmentLayout.setOnClickListener(v -> {
Log.d(TAG, "layoutInfo was clicked");
@ -78,7 +83,7 @@ public class ExternalPlayerFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController();
butPlay.setOnClickListener(v -> {
if(controller != null) {
if (controller != null) {
controller.playPause();
}
});
@ -127,8 +132,9 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
controller.init();
onPositionObserverUpdate();
controller.init();
}
@Override
@ -138,6 +144,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null) {
controller.release();
}
if (disposable != null) {
disposable.dispose();
}
}
@Override
@ -158,7 +167,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(v -> {
if(controller != null) {
if (controller != null) {
controller.playPause();
}
});
@ -173,7 +182,25 @@ public class ExternalPlayerFragment extends Fragment {
return false;
}
Playable media = controller.getMedia();
if (disposable != null) {
disposable.dispose();
}
disposable = Maybe.create(emitter -> {
Playable media = controller.getMedia();
if (media != null) {
emitter.onSuccess(media);
} else {
emitter.onComplete();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(media -> updateUi((Playable) media),
error -> Log.e(TAG, Log.getStackTraceString(error)));
return true;
}
private void updateUi(Playable media) {
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
mFeedName.setText(media.getFeedTitle());
@ -181,11 +208,12 @@ public class ExternalPlayerFragment extends Fragment {
Glide.with(getActivity())
.load(media.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
@ -194,18 +222,11 @@ public class ExternalPlayerFragment extends Fragment {
} else {
butPlay.setVisibility(View.VISIBLE);
}
return true;
} else {
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
return false;
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
}
}
private String getPositionString(int position, int duration) {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
}
public PlaybackController getPlaybackControllerTestingOnly() {
return controller;
}

View File

@ -8,6 +8,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
@ -50,6 +51,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
R.layout.all_episodes_fragment);
((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_fav_episodes_head_label);
((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_fav_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
@ -62,8 +65,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
Log.d(TAG, "remove(" + holder.getItemId() + ")");
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
FeedItem item = holder.getFeedItem();
if (item != null) {

View File

@ -28,9 +28,9 @@ import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.mfietz.fyydlin.FyydClient;
import de.mfietz.fyydlin.FyydResponse;
import de.mfietz.fyydlin.SearchHit;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
import static java.util.Collections.emptyList;
@ -55,7 +55,7 @@ public class FyydSearchFragment extends Fragment {
* List of podcasts retreived from the search
*/
private List<Podcast> searchResults;
private Subscription subscription;
private Disposable disposable;
/**
* Constructor
@ -75,7 +75,7 @@ public class FyydSearchFragment extends Fragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
gridView = (GridView) root.findViewById(R.id.gridView);
gridView = root.findViewById(R.id.gridView);
adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
gridView.setAdapter(adapter);
@ -87,10 +87,10 @@ public class FyydSearchFragment extends Fragment {
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
startActivity(intent);
});
progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
txtvError = (TextView) root.findViewById(R.id.txtvError);
butRetry = (Button) root.findViewById(R.id.butRetry);
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
progressBar = root.findViewById(R.id.progressBar);
txtvError = root.findViewById(R.id.txtvError);
butRetry = root.findViewById(R.id.butRetry);
txtvEmpty = root.findViewById(android.R.id.empty);
return root;
}
@ -98,8 +98,8 @@ public class FyydSearchFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
adapter = null;
}
@ -141,12 +141,12 @@ public class FyydSearchFragment extends Fragment {
}
private void search(String query) {
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
showOnlyProgressBar();
subscription = client.searchPodcasts(query)
.subscribeOn(Schedulers.newThread())
disposable = client.searchPodcasts(query, 10)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
progressBar.setVisibility(View.GONE);
@ -174,7 +174,7 @@ public class FyydSearchFragment extends Fragment {
if (!response.getData().isEmpty()) {
adapter.clear();
searchResults = new ArrayList<>();
for (SearchHit searchHit : response.getData().values()) {
for (SearchHit searchHit : response.getData()) {
Podcast podcast = Podcast.fromSearch(searchHit);
searchResults.add(podcast);
}

View File

@ -11,6 +11,7 @@ import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
@ -39,10 +40,10 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays the description of a Playable object in a Webview.
@ -66,7 +67,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private ShownotesProvider shownotesProvider;
private Playable media;
private Subscription webViewLoader;
private Disposable webViewLoader;
/**
* URL that was selected via long-press.
@ -113,10 +114,13 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
Log.d(TAG, "Creating view");
webvDescription = new WebView(getActivity().getApplicationContext());
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[]
{android.R.attr.colorBackground});
int backgroundColor = ta.getColor(0, UserPreferences.getTheme() ==
R.style.Theme_AntennaPod_Dark ? Color.BLACK : Color.WHITE);
boolean black = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark
|| UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack;
int backgroundColor = ta.getColor(0, black ? Color.BLACK : Color.WHITE);
ta.recycle();
webvDescription.setBackgroundColor(backgroundColor);
if (!NetworkUtils.networkAvailable()) {
@ -164,7 +168,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
super.onDestroy();
Log.d(TAG, "Fragment destroyed");
if (webViewLoader != null) {
webViewLoader.unsubscribe();
webViewLoader.dispose();
}
if (webvDescription != null) {
webvDescription.removeAllViews();
@ -195,7 +199,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
} else if (args.containsKey(ARG_FEEDITEM_ID)) {
long id = getArguments().getLong(ARG_FEEDITEM_ID);
Observable.defer(() -> Observable.just(DBReader.getFeedItem(id)))
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(feedItem -> {
shownotesProvider = feedItem;
@ -295,21 +299,22 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private void load() {
Log.d(TAG, "load()");
if(webViewLoader != null) {
webViewLoader.unsubscribe();
webViewLoader.dispose();
}
if(shownotesProvider == null) {
return;
}
webViewLoader = Observable.defer(() -> Observable.just(loadData()))
.subscribeOn(Schedulers.newThread())
webViewLoader = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
webvDescription.loadDataWithBaseURL(null, data, "text/html",
webvDescription.loadDataWithBaseURL("https://127.0.0.1", data, "text/html",
"utf-8", "about:blank");
Log.d(TAG, "Webview loaded");
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@NonNull
private String loadData() {
Timeline timeline = new Timeline(getActivity(), shownotesProvider);
return timeline.processShownotes(highlightTimecodes);

View File

@ -31,11 +31,10 @@ import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.NetworkUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
@ -53,26 +52,27 @@ 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.service.download.Downloader;
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.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.OnSwipeGesture;
import de.danoeh.antennapod.view.SwipeGestureDetector;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a FeedItem and actions.
@ -135,7 +135,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private IconButton butAction2;
private Menu popupMenu;
private Subscription subscription;
private Disposable disposable;
/**
* URL that was selected via long-press.
@ -166,26 +166,27 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
super.onCreateView(inflater, container, savedInstanceState);
View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
root = (ViewGroup) layout.findViewById(R.id.content_root);
root = layout.findViewById(R.id.content_root);
LinearLayout header = (LinearLayout) root.findViewById(R.id.header);
LinearLayout header = root.findViewById(R.id.header);
if(feedItems.length > 0) {
header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
}
txtvPodcast = (TextView) layout.findViewById(R.id.txtvPodcast);
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle);
txtvTitle = layout.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
txtvDuration = (TextView) layout.findViewById(R.id.txtvDuration);
txtvPublished = (TextView) layout.findViewById(R.id.txtvPublished);
txtvDuration = layout.findViewById(R.id.txtvDuration);
txtvPublished = layout.findViewById(R.id.txtvPublished);
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
webvDescription = layout.findViewById(R.id.webvDescription);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark ||
UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@ -215,12 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
});
registerForContextMenu(webvDescription);
imgvCover = (ImageView) layout.findViewById(R.id.imgvCover);
imgvCover = layout.findViewById(R.id.imgvCover);
imgvCover.setOnClickListener(v -> openPodcast());
progbarDownload = (ProgressBar) layout.findViewById(R.id.progbarDownload);
progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
progbarDownload = layout.findViewById(R.id.progbarDownload);
progbarLoading = layout.findViewById(R.id.progbarLoading);
butAction1 = layout.findViewById(R.id.butAction1);
butAction2 = layout.findViewById(R.id.butAction2);
butAction1.setOnClickListener(v -> {
if (item == null) {
@ -285,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onDestroyView() {
super.onDestroyView();
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
if (webvDescription != null && root != null) {
root.removeView(webvDescription);
@ -357,7 +358,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private void onFragmentLoaded() {
if (webviewData != null) {
webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank");
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
}
updateAppearance();
}
@ -378,11 +379,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Glide.with(getActivity())
.load(item.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(imgvCover);
progbarDownload.setVisibility(View.GONE);
@ -435,7 +437,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
FeedItem.State state = item.getState();
if (butAction2Text == R.string.delete_label && state == FeedItem.State.PLAYING) {
if (butAction2Text == R.string.delete_label && state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
butAction2.setEnabled(false);
butAction2.setAlpha(0.5f);
} else {
@ -571,12 +573,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
};
private void load() {
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
subscription = Observable.fromCallable(this::loadInBackground)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(this::loadInBackground)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
progbarLoading.setVisibility(View.GONE);
@ -586,6 +588,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@Nullable
private FeedItem loadInBackground() {
FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
if (feedItem != null) {

View File

@ -4,10 +4,9 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
@ -26,18 +25,17 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.IconDrawable;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.activity.FeedSettingsActivity;
import org.apache.commons.lang3.Validate;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.FeedInfoActivity;
import de.danoeh.antennapod.activity.FeedSettingsActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
@ -55,7 +53,6 @@ import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
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.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@ -71,10 +68,10 @@ import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Displays a list of FeedItems.
@ -114,7 +111,7 @@ public class ItemlistFragment extends ListFragment {
private TextView txtvInformation;
private Subscription subscription;
private Disposable disposable;
/**
* Creates new ItemlistFragment which shows the Feeditems of a specific
@ -165,8 +162,8 @@ public class ItemlistFragment extends ListFragment {
super.onPause();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
}
@ -417,13 +414,10 @@ public class ItemlistFragment extends ListFragment {
}
private boolean insideOnFragmentLoaded = false;
private void onFragmentLoaded() {
if(!isVisible()) {
return;
}
insideOnFragmentLoaded = true;
if (adapter == null) {
setListAdapter(null);
setupHeaderView();
@ -440,9 +434,6 @@ public class ItemlistFragment extends ListFragment {
if (feed != null && feed.getNextPageLink() == null && listFooter != null) {
getListView().removeFooterView(listFooter.getRoot());
}
insideOnFragmentLoaded = false;
}
private void refreshHeaderView() {
@ -486,14 +477,14 @@ public class ItemlistFragment extends ListFragment {
View header = inflater.inflate(R.layout.feeditemlist_header, lv, false);
lv.addHeaderView(header);
txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground);
imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo);
ImageButton butShowSettings = (ImageButton) header.findViewById(R.id.butShowSettings);
txtvInformation = (TextView) header.findViewById(R.id.txtvInformation);
txtvFailure = (IconTextView) header.findViewById(R.id.txtvFailure);
txtvTitle = header.findViewById(R.id.txtvTitle);
TextView txtvAuthor = header.findViewById(R.id.txtvAuthor);
imgvBackground = header.findViewById(R.id.imgvBackground);
imgvCover = header.findViewById(R.id.imgvCover);
ImageButton butShowInfo = header.findViewById(R.id.butShowInfo);
ImageButton butShowSettings = header.findViewById(R.id.butShowSettings);
txtvInformation = header.findViewById(R.id.txtvInformation);
txtvFailure = header.findViewById(R.id.txtvFailure);
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
@ -529,20 +520,22 @@ public class ItemlistFragment extends ListFragment {
private void loadFeedImage() {
Glide.with(getActivity())
.load(feed.getImageLocation())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation(getActivity()))
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.transform(new FastBlurTransformation())
.dontAnimate())
.into(imgvBackground);
Glide.with(getActivity())
.load(feed.getImageLocation())
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(imgvCover);
}
@ -618,23 +611,22 @@ public class ItemlistFragment extends ListFragment {
private void loadItems() {
if(subscription != null) {
subscription.unsubscribe();
if(disposable != null) {
disposable.dispose();
}
subscription = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.newThread())
disposable = Observable.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
feed = result;
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
}
feed = result;
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@Nullable
private Feed loadData() {
Feed feed = DBReader.getFeed(feedID);
DBReader.loadAdditionalFeedItemListData(feed.getItems());

View File

@ -36,13 +36,14 @@ import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
@ -69,7 +70,7 @@ public class ItunesSearchFragment extends Fragment {
*/
private List<Podcast> searchResults;
private List<Podcast> topList;
private Subscription subscription;
private Disposable disposable;
/**
* Replace adapter data with provided search results from SearchTask.
@ -109,7 +110,7 @@ public class ItunesSearchFragment extends Fragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
gridView = (GridView) root.findViewById(R.id.gridView);
gridView = root.findViewById(R.id.gridView);
adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
gridView.setAdapter(adapter);
@ -127,7 +128,7 @@ public class ItunesSearchFragment extends Fragment {
} else {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = Observable.create((Observable.OnSubscribe<String>) subscriber -> {
disposable = Single.create((SingleOnSubscribe<String>) emitter -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
.url(podcast.feedUrl)
@ -139,17 +140,16 @@ public class ItunesSearchFragment extends Fragment {
JSONObject result = new JSONObject(resultString);
JSONObject results = result.getJSONArray("results").getJSONObject(0);
String feedUrl = results.getString("feedUrl");
subscriber.onNext(feedUrl);
emitter.onSuccess(feedUrl);
} else {
String prefix = getString(R.string.error_msg_prefix);
subscriber.onError(new IOException(prefix + response));
emitter.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
subscriber.onError(e);
emitter.onError(e);
}
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(feedUrl -> {
progressBar.setVisibility(View.GONE);
@ -170,10 +170,10 @@ public class ItunesSearchFragment extends Fragment {
});
}
});
progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
txtvError = (TextView) root.findViewById(R.id.txtvError);
butRetry = (Button) root.findViewById(R.id.butRetry);
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
progressBar = root.findViewById(R.id.progressBar);
txtvError = root.findViewById(R.id.txtvError);
butRetry = root.findViewById(R.id.butRetry);
txtvEmpty = root.findViewById(android.R.id.empty);
loadToplist();
@ -183,8 +183,8 @@ public class ItunesSearchFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
adapter = null;
}
@ -228,15 +228,15 @@ public class ItunesSearchFragment extends Fragment {
}
private void loadToplist() {
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> {
String lang = Locale.getDefault().getLanguage();
String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
OkHttpClient client = AntennapodHttpClient.getHttpClient();
@ -268,15 +268,14 @@ public class ItunesSearchFragment extends Fragment {
}
else {
String prefix = getString(R.string.error_msg_prefix);
subscriber.onError(new IOException(prefix + response));
emitter.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
subscriber.onError(e);
emitter.onError(e);
}
subscriber.onNext(results);
subscriber.onCompleted();
emitter.onSuccess(results);
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);
@ -293,15 +292,15 @@ public class ItunesSearchFragment extends Fragment {
}
private void search(String query) {
if (subscription != null) {
subscription.unsubscribe();
if (disposable != null) {
disposable.dispose();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
disposable = Single.create((SingleOnSubscribe<List<Podcast>>) subscriber -> {
String encodedQuery = null;
try {
encodedQuery = URLEncoder.encode(query, "UTF-8");
@ -312,8 +311,7 @@ public class ItunesSearchFragment extends Fragment {
encodedQuery = query; // failsafe
}
//Spaces in the query need to be replaced with '+' character.
String formattedUrl = String.format(API_URL, query).replace(' ', '+');
String formattedUrl = String.format(API_URL, encodedQuery);
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
@ -341,10 +339,9 @@ public class ItunesSearchFragment extends Fragment {
} catch (IOException | JSONException e) {
subscriber.onError(e);
}
subscriber.onNext(podcasts);
subscriber.onCompleted();
subscriber.onSuccess(podcasts);
})
.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);

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