Merge branch 'develop' into notification-default-icon
This commit is contained in:
commit
71a5a00fd0
|
@ -1,49 +1,77 @@
|
|||
version: 2
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-debug:
|
||||
docker:
|
||||
- image: circleci/android:api-28
|
||||
|
||||
working_directory: ~/AntennaPod
|
||||
|
||||
environment:
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-android-{{ checksum "build.gradle" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v1-android-
|
||||
|
||||
- run:
|
||||
name: Create temporary release keystore
|
||||
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"
|
||||
|
||||
- run:
|
||||
name: Build debug
|
||||
command: ./gradlew assembleDebug -PdisablePreDex
|
||||
- run:
|
||||
name: Execute debug unit tests
|
||||
command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.android
|
||||
- ~/.gradle
|
||||
- ~/android
|
||||
key: v1-android-{{ checksum "build.gradle" }}
|
||||
|
||||
test-release:
|
||||
docker:
|
||||
- image: circleci/android:api-28
|
||||
working_directory: ~/AntennaPod
|
||||
environment:
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-android-{{ checksum "build.gradle" }}
|
||||
- v1-android-
|
||||
- run:
|
||||
name: Create temporary release keystore
|
||||
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"
|
||||
- run:
|
||||
name: Build release
|
||||
command: ./gradlew assembleRelease -PdisablePreDex
|
||||
|
||||
- run:
|
||||
name: Execute unit tests
|
||||
name: Execute release unit tests
|
||||
command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.android
|
||||
- ~/.gradle
|
||||
- ~/android
|
||||
key: v1-android-{{ checksum "build.gradle" }}
|
||||
|
||||
build-androidtest:
|
||||
docker:
|
||||
- image: circleci/android:api-28
|
||||
working_directory: ~/AntennaPod
|
||||
environment:
|
||||
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
_JAVA_OPTIONS: "-Xms256m -Xmx1280m"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-android-{{ checksum "build.gradle" }}
|
||||
- v1-android-
|
||||
- run:
|
||||
name: Build integration tests
|
||||
command: ./gradlew :app:assemblePlayDebugAndroidTest -PdisablePreDex
|
||||
|
||||
- store_artifacts:
|
||||
path: app/build/outputs/apk
|
||||
destination: apks
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.android
|
||||
|
@ -55,10 +83,8 @@ jobs:
|
|||
docker:
|
||||
- image: circleci/android:api-28
|
||||
working_directory: ~/AntennaPod
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Checkstyle
|
||||
command: ./gradlew checkstyle
|
||||
|
@ -66,10 +92,12 @@ jobs:
|
|||
workflows:
|
||||
version: 2
|
||||
|
||||
test:
|
||||
unit-tests:
|
||||
jobs:
|
||||
- test
|
||||
- test-debug
|
||||
- test-release
|
||||
- build-androidtest
|
||||
|
||||
checkstyle:
|
||||
static-analysis:
|
||||
jobs:
|
||||
- checkstyle
|
||||
|
|
|
@ -18,8 +18,8 @@ android {
|
|||
// "1.2.3-SNAPSHOT" -> 1020300
|
||||
// "1.2.3-RC4" -> 1020304
|
||||
// "1.2.3" -> 1020395
|
||||
versionCode 1070395
|
||||
versionName "1.7.3"
|
||||
versionCode 1070396
|
||||
versionName "1.7.3b"
|
||||
testApplicationId "de.test.antennapod"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
generatedDensities = []
|
||||
|
@ -139,10 +139,6 @@ dependencies {
|
|||
implementation "com.android.support:recyclerview-v7:$supportVersion"
|
||||
compileOnly 'com.google.android.wearable:wearable:2.2.0'
|
||||
|
||||
// ViewModel and LiveData
|
||||
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
|
||||
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
|
||||
|
||||
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
|
||||
implementation "commons-io:commons-io:$commonsioVersion"
|
||||
implementation "org.jsoup:jsoup:$jsoupVersion"
|
||||
|
@ -173,8 +169,8 @@ dependencies {
|
|||
|
||||
implementation 'com.github.mfietz:fyydlin:v0.4.2'
|
||||
implementation 'com.github.ByteHamster:SearchPreference:v1.3.0'
|
||||
implementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||
|
||||
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||
androidTestImplementation 'com.nanohttpd:nanohttpd-webserver:2.1.1'
|
||||
androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package de.danoeh.antennapod.core.service.download;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import de.danoeh.antennapod.core.util.Consumer;
|
||||
|
||||
public class StubDownloader extends Downloader {
|
||||
|
||||
private final long downloadTime;
|
||||
|
||||
@NonNull
|
||||
private final Consumer<DownloadStatus> onDownloadComplete;
|
||||
|
||||
public StubDownloader(@NonNull DownloadRequest request, long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
|
||||
super(request);
|
||||
this.downloadTime = downloadTime;
|
||||
this.onDownloadComplete = onDownloadComplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void download() {
|
||||
try {
|
||||
Thread.sleep(downloadTime);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
onDownloadComplete.accept(result);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DownloadRequest getDownloadRequest() {
|
||||
return super.getDownloadRequest();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DownloadStatus getResult() {
|
||||
return super.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return super.isFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
result.setCancelled();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package de.test.antennapod.service.download;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.awaitility.core.ConditionTimeoutException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.service.download.StubDownloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Consumer;
|
||||
|
||||
import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @see HttpDownloaderTest for the test of actual download (and saving the file)
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DownloadServiceTest {
|
||||
|
||||
private CountDownLatch latch = null;
|
||||
private Feed testFeed = null;
|
||||
private FeedMedia testMedia11 = null;
|
||||
|
||||
private DownloadService.DownloaderFactory origFactory = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
origFactory = DownloadService.getDownloaderFactory();
|
||||
testFeed = setUpTestFeeds();
|
||||
testMedia11 = testFeed.getItemAtIndex(0).getMedia();
|
||||
}
|
||||
|
||||
private Feed setUpTestFeeds() throws Exception {
|
||||
Feed feed = new Feed("url", null, "Test Feed title 1");
|
||||
List<FeedItem> items = new ArrayList<>();
|
||||
feed.setItems(items);
|
||||
FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
|
||||
items.add(item1);
|
||||
FeedMedia media1 = new FeedMedia(0, item1, 123, 1, 1, "audio/mp3", null, "http://example.com/episode.mp3", false, null, 0, 0);
|
||||
item1.setMedia(media1);
|
||||
|
||||
DBWriter.setFeedItem(item1).get();
|
||||
return feed;
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
DownloadService.setDownloaderFactory(origFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventsGeneratedCaseMediaDownloadSuccess() throws Exception {
|
||||
// create a stub download that returns successful
|
||||
//
|
||||
// OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
|
||||
// are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
|
||||
// to do so
|
||||
DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
|
||||
downloadStatus.setSuccessful();
|
||||
}));
|
||||
|
||||
withFeedItemEventListener(feedItemEventListener -> {
|
||||
try {
|
||||
assertEquals(0, feedItemEventListener.getEvents().size());
|
||||
assertFalse("The media in test should not yet been downloaded",
|
||||
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
|
||||
|
||||
DownloadRequester.getInstance().downloadMedia(InstrumentationRegistry.getTargetContext(),
|
||||
testMedia11);
|
||||
Awaitility.await()
|
||||
.atMost(1000, TimeUnit.MILLISECONDS)
|
||||
.until(() -> feedItemEventListener.getEvents().size() > 0);
|
||||
assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
|
||||
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
|
||||
} catch (ConditionTimeoutException cte) {
|
||||
fail("The expected FeedItemEvent (for media download complete) has not been posted. "
|
||||
+ cte.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class StubDownloaderFactory implements DownloadService.DownloaderFactory {
|
||||
private final long downloadTime;
|
||||
|
||||
@NonNull
|
||||
private final Consumer<DownloadStatus> onDownloadComplete;
|
||||
|
||||
StubDownloaderFactory(long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
|
||||
this.downloadTime = downloadTime;
|
||||
this.onDownloadComplete = onDownloadComplete;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Downloader create(@NonNull DownloadRequest request) {
|
||||
return new StubDownloader(request, downloadTime, onDownloadComplete);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,12 @@ import android.support.test.InstrumentationRegistry;
|
|||
import android.support.test.annotation.UiThreadTest;
|
||||
import android.support.test.filters.LargeTest;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
@ -15,14 +21,14 @@ import de.danoeh.antennapod.core.event.QueueEvent;
|
|||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
|
||||
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.playback.Playable;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -123,6 +129,43 @@ public class PlaybackServiceTaskManagerTest {
|
|||
pstm.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueueUpdatedUponDownloadComplete() throws Exception {
|
||||
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
{ // Setup test data
|
||||
List<FeedItem> queue = writeTestQueue("a");
|
||||
FeedItem item = DBReader.getFeedItem(queue.get(0).getId());
|
||||
FeedMedia media = new FeedMedia(item, "http://example.com/episode.mp3", 12345, "audio/mp3");
|
||||
item.setMedia(media);
|
||||
DBWriter.setFeedMedia(media).get();
|
||||
DBWriter.setFeedItem(item).get();
|
||||
}
|
||||
|
||||
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
|
||||
final FeedItem testItem = pstm.getQueue().get(0);
|
||||
assertFalse("The item should not yet be downloaded", testItem.getMedia().isDownloaded());
|
||||
|
||||
withFeedItemEventListener( feedItemEventListener -> {
|
||||
// simulate download complete (in DownloadService.MediaHandlerThread)
|
||||
FeedItem item = DBReader.getFeedItem(testItem.getId());
|
||||
item.getMedia().setDownloaded(true);
|
||||
item.getMedia().setFile_url("file://123");
|
||||
item.setAutoDownload(false);
|
||||
DBWriter.setFeedMedia(item.getMedia()).get();
|
||||
DBWriter.setFeedItem(item).get();
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(1000, TimeUnit.MILLISECONDS)
|
||||
.until(() -> feedItemEventListener.getEvents().size() > 0);
|
||||
|
||||
final FeedItem itemUpdated = pstm.getQueue().get(0);
|
||||
assertTrue("media.isDownloaded() should be true - The queue in PlaybackService should be updated after download is completed",
|
||||
itemUpdated.getMedia().isDownloaded());
|
||||
});
|
||||
|
||||
pstm.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartPositionSaver() throws InterruptedException {
|
||||
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package de.test.antennapod.util.event;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.event.FeedItemEvent;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
/**
|
||||
* Test helpers to listen {@link FeedItemEvent} and handle them accordingly
|
||||
*
|
||||
*/
|
||||
public class FeedItemEventListener {
|
||||
private final List<FeedItemEvent> events = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Provides an listener subscribing to {@link FeedItemEvent} that the callers can use
|
||||
*
|
||||
* Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
|
||||
*/
|
||||
public static void withFeedItemEventListener(@NonNull Consumer<FeedItemEventListener> consumer)
|
||||
throws Exception {
|
||||
FeedItemEventListener feedItemEventListener = new FeedItemEventListener();
|
||||
try {
|
||||
EventBus.getDefault().register(feedItemEventListener);
|
||||
consumer.accept(feedItemEventListener);
|
||||
} finally {
|
||||
EventBus.getDefault().unregister(feedItemEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEvent(FeedItemEvent event) {
|
||||
events.add(event);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<? extends FeedItemEvent> getEvents() {
|
||||
return events;
|
||||
}
|
||||
}
|
|
@ -105,17 +105,6 @@
|
|||
android:value="de.danoeh.antennapod.activity.MainActivity"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.FeedInfoActivity"
|
||||
android:label="@string/feed_info_label">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.FeedSettingsActivity"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:label="@string/feed_settings_label">
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".core.service.PlayerWidgetJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
|
@ -150,13 +139,6 @@
|
|||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.StatisticsActivity"
|
||||
android:label="@string/statistics_label">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.ImportExportActivity"
|
||||
android:label="@string/import_export">
|
||||
|
|
|
@ -63,6 +63,8 @@ public class PodcastApp extends Application {
|
|||
EventBus.builder()
|
||||
.addIndex(new ApEventBusIndex())
|
||||
.addIndex(new ApCoreEventBusIndex())
|
||||
.logNoSubscriberMessages(false)
|
||||
.sendNoSubscriberEvent(false)
|
||||
.installDefaultEventBus();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.webkit.WebView;
|
|||
import android.webkit.WebViewClient;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -57,8 +58,7 @@ public class AboutActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.startsWith("http")) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(browserIntent);
|
||||
IntentUtils.openInBrowser(AboutActivity.this, url);
|
||||
return true;
|
||||
} else {
|
||||
url = url.replace("file:///android_asset/", "");
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
package de.danoeh.antennapod.activity;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import de.danoeh.antennapod.CrashReportWriter;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -45,13 +42,7 @@ public class BugReportActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
|
||||
try {
|
||||
Intent myIntent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("https://github.com/AntennaPod/AntennaPod/issues"));
|
||||
startActivity(myIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
|
||||
});
|
||||
|
||||
findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
package de.danoeh.antennapod.activity;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.LightingColorFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
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;
|
||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.LangUtils;
|
||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||
import 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.
|
||||
*/
|
||||
public class FeedInfoActivity extends AppCompatActivity {
|
||||
|
||||
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
|
||||
private static final String TAG = "FeedInfoActivity";
|
||||
private Feed feed;
|
||||
|
||||
private ImageView imgvCover;
|
||||
private TextView txtvTitle;
|
||||
private TextView txtvDescription;
|
||||
private TextView lblLanguage;
|
||||
private TextView txtvLanguage;
|
||||
private TextView lblAuthor;
|
||||
private TextView txtvAuthor;
|
||||
private TextView txtvUrl;
|
||||
|
||||
private Disposable disposable;
|
||||
|
||||
|
||||
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) FeedInfoActivity.this
|
||||
.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(clipData);
|
||||
Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT);
|
||||
t.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(UserPreferences.getTheme());
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.feedinfo);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
|
||||
|
||||
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 = 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);
|
||||
|
||||
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 -> {
|
||||
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())
|
||||
.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())
|
||||
.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());
|
||||
|
||||
String description = feed.getDescription();
|
||||
if(description != null) {
|
||||
if(Feed.TYPE_ATOM1.equals(feed.getType())) {
|
||||
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||
Document feedDescription = Jsoup.parse(feed.getDescription());
|
||||
description = StringUtils.trim(formatter.getPlainText(feedDescription));
|
||||
}
|
||||
} else {
|
||||
description = "";
|
||||
}
|
||||
txtvDescription.setText(description);
|
||||
|
||||
if (!TextUtils.isEmpty(feed.getAuthor())) {
|
||||
txtvAuthor.setText(feed.getAuthor());
|
||||
txtvAuthorHeader.setText(feed.getAuthor());
|
||||
} else {
|
||||
lblAuthor.setVisibility(View.GONE);
|
||||
txtvAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(feed.getLanguage())) {
|
||||
txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage()));
|
||||
} else {
|
||||
lblLanguage.setVisibility(View.GONE);
|
||||
txtvLanguage.setVisibility(View.GONE);
|
||||
}
|
||||
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
|
||||
Iconify.addIcons(txtvUrl);
|
||||
|
||||
supportInvalidateOptionsMenu();
|
||||
}, 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 (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.feedinfo, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
|
||||
menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
|
||||
IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
try {
|
||||
return FeedMenuHandler.onOptionsItemClicked(this, item, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this,
|
||||
e.getMessage());
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package de.danoeh.antennapod.activity;
|
||||
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.graphics.LightingColorFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
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.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.fragment.FeedSettingsFragment;
|
||||
import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Displays information about a feed.
|
||||
*/
|
||||
public class FeedSettingsActivity extends AppCompatActivity {
|
||||
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
|
||||
private static final String TAG = "FeedSettingsActivity";
|
||||
private Feed feed;
|
||||
private Disposable disposable;
|
||||
private ImageView imgvCover;
|
||||
private TextView txtvTitle;
|
||||
private ImageView imgvBackground;
|
||||
private TextView txtvAuthorHeader;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(UserPreferences.getTheme());
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.feedsettings);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
imgvCover = findViewById(R.id.imgvCover);
|
||||
txtvTitle = findViewById(R.id.txtvTitle);
|
||||
txtvAuthorHeader = findViewById(R.id.txtvAuthor);
|
||||
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));
|
||||
|
||||
long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
|
||||
disposable = ViewModelProviders.of(this).get(FeedSettingsViewModel.class).getFeed(feedId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
feed = result;
|
||||
showFragment();
|
||||
showHeader();
|
||||
}, error -> {
|
||||
Log.d(TAG, Log.getStackTraceString(error));
|
||||
finish();
|
||||
}, () -> {
|
||||
Log.e(TAG, "Activity was started with invalid arguments");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void showFragment() {
|
||||
FeedSettingsFragment fragment = new FeedSettingsFragment();
|
||||
fragment.setArguments(getIntent().getExtras());
|
||||
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
FragmentTransaction fragmentTransaction =
|
||||
fragmentManager.beginTransaction();
|
||||
fragmentTransaction.replace(R.id.settings_fragment_container, fragment);
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
|
||||
private void showHeader() {
|
||||
txtvTitle.setText(feed.getTitle());
|
||||
|
||||
if (!TextUtils.isEmpty(feed.getAuthor())) {
|
||||
txtvAuthorHeader.setText(feed.getAuthor());
|
||||
}
|
||||
|
||||
Glide.with(FeedSettingsActivity.this)
|
||||
.load(feed.getImageLocation())
|
||||
.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())
|
||||
.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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,6 @@ import de.danoeh.antennapod.adapter.NavListAdapter;
|
|||
import de.danoeh.antennapod.core.asynctask.FeedRemover;
|
||||
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
||||
import de.danoeh.antennapod.core.event.MessageEvent;
|
||||
import de.danoeh.antennapod.core.event.ProgressEvent;
|
||||
import de.danoeh.antennapod.core.event.QueueEvent;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
|
@ -70,6 +69,7 @@ import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
|||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||
import de.danoeh.antennapod.fragment.TransitionEffect;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
import de.danoeh.antennapod.preferences.PreferenceUpgrader;
|
||||
import io.reactivex.Observable;
|
||||
|
@ -378,15 +378,34 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||
}
|
||||
}
|
||||
|
||||
public void loadChildFragment(Fragment fragment) {
|
||||
public void loadChildFragment(Fragment fragment, TransitionEffect transition) {
|
||||
Validate.notNull(fragment);
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
fm.beginTransaction()
|
||||
.replace(R.id.main_view, fragment, "main")
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
|
||||
switch (transition) {
|
||||
case FADE:
|
||||
transaction.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
|
||||
break;
|
||||
case FLIP:
|
||||
transaction.setCustomAnimations(
|
||||
R.anim.card_flip_left_in,
|
||||
R.anim.card_flip_left_out,
|
||||
R.anim.card_flip_right_in,
|
||||
R.anim.card_flip_right_out);
|
||||
break;
|
||||
}
|
||||
|
||||
transaction
|
||||
.hide(getSupportFragmentManager().findFragmentByTag("main"))
|
||||
.add(R.id.main_view, fragment, "main")
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void loadChildFragment(Fragment fragment) {
|
||||
loadChildFragment(fragment, TransitionEffect.NONE);
|
||||
}
|
||||
|
||||
public void dismissChildFragment() {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
|
@ -787,25 +806,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||
loadData();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(ProgressEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
switch(event.action) {
|
||||
case START:
|
||||
pd = new ProgressDialog(this);
|
||||
pd.setMessage(event.message);
|
||||
pd.setIndeterminate(true);
|
||||
pd.setCancelable(false);
|
||||
pd.show();
|
||||
break;
|
||||
case END:
|
||||
if(pd != null) {
|
||||
pd.dismiss();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(MessageEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.joanzapata.iconify.IconDrawable;
|
|||
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
|
@ -63,6 +64,9 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -194,6 +198,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||
};
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
onPositionObserverUpdate();
|
||||
}
|
||||
|
||||
private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
|
||||
return activity.txtvFF;
|
||||
}
|
||||
|
@ -274,6 +283,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||
controller.init();
|
||||
loadMediaInfo();
|
||||
onPositionObserverUpdate();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -286,6 +296,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
EventBus.getDefault().unregister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
|
@ -452,8 +463,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
|
|||
}
|
||||
break;
|
||||
case R.id.visit_website_item:
|
||||
Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, uri));
|
||||
IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
|
||||
break;
|
||||
case R.id.share_link_item:
|
||||
if (feedItem != null) {
|
||||
|
|
|
@ -122,7 +122,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
|
|||
disposable.dispose();
|
||||
}
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
saveCurrentFragment();
|
||||
}
|
||||
|
||||
|
@ -175,7 +174,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
|
|||
protected void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.danoeh.antennapod.adapter;
|
||||
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
@ -23,10 +24,12 @@ import android.widget.TextView;
|
|||
import com.joanzapata.iconify.Iconify;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
|
@ -50,6 +53,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
|
|||
private final boolean showOnlyNewEpisodes;
|
||||
|
||||
private FeedItem selectedItem;
|
||||
private Holder currentlyPlayingItem = null;
|
||||
|
||||
private final int playingBackGroundColor;
|
||||
private final int normalBackGroundColor;
|
||||
|
@ -165,8 +169,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
|
|||
holder.progress.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
if(media.isCurrentlyPlaying()) {
|
||||
if (media.isCurrentlyPlaying()) {
|
||||
holder.container.setBackgroundColor(playingBackGroundColor);
|
||||
currentlyPlayingItem = holder;
|
||||
} else {
|
||||
holder.container.setBackgroundColor(normalBackGroundColor);
|
||||
}
|
||||
|
@ -196,6 +201,22 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
|
|||
.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) {
|
||||
onBindViewHolder(holder, pos);
|
||||
|
||||
if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
|
||||
PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
|
||||
holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
|
||||
if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FeedItem getSelectedItem() {
|
||||
return selectedItem;
|
||||
|
|
|
@ -13,15 +13,18 @@ import android.widget.BaseAdapter;
|
|||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
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.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.ThemeUtils;
|
||||
|
@ -39,6 +42,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||
private final int playingBackGroundColor;
|
||||
private final int normalBackGroundColor;
|
||||
|
||||
private int currentlyPlayingItem = -1;
|
||||
|
||||
public FeedItemlistAdapter(Context context,
|
||||
ItemAccess itemAccess,
|
||||
boolean showFeedtitle,
|
||||
|
@ -176,8 +181,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||
}
|
||||
typeDrawables.recycle();
|
||||
|
||||
if(media.isCurrentlyPlaying()) {
|
||||
if (media.isCurrentlyPlaying()) {
|
||||
holder.container.setBackgroundColor(playingBackGroundColor);
|
||||
currentlyPlayingItem = position;
|
||||
} else {
|
||||
holder.container.setBackgroundColor(normalBackGroundColor);
|
||||
}
|
||||
|
@ -195,6 +201,19 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||
return convertView;
|
||||
}
|
||||
|
||||
public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) {
|
||||
if (currentlyPlayingItem != -1 && currentlyPlayingItem < getCount()) {
|
||||
View view = listView.getChildAt(currentlyPlayingItem
|
||||
- listView.getFirstVisiblePosition() + listView.getHeaderViewsCount());
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
Holder holder = (Holder) view.getTag();
|
||||
holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
||||
holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
static class Holder {
|
||||
LinearLayout container;
|
||||
TextView title;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.danoeh.antennapod.adapter;
|
||||
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
|
@ -24,9 +25,11 @@ import android.widget.TextView;
|
|||
|
||||
import com.joanzapata.iconify.Iconify;
|
||||
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
|
@ -57,6 +60,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
|||
private boolean locked;
|
||||
|
||||
private FeedItem selectedItem;
|
||||
private ViewHolder currentlyPlayingItem = null;
|
||||
|
||||
private final int playingBackGroundColor;
|
||||
private final int normalBackGroundColor;
|
||||
|
@ -93,6 +97,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) {
|
||||
onBindViewHolder(holder, pos);
|
||||
|
||||
if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
|
||||
PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
|
||||
holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
|
||||
holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
|
||||
holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FeedItem getSelectedItem() {
|
||||
return selectedItem;
|
||||
|
@ -108,6 +124,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
|||
return itemAccess.getCount();
|
||||
}
|
||||
|
||||
public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
|
||||
if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder
|
||||
implements View.OnClickListener,
|
||||
View.OnCreateContextMenuListener,
|
||||
|
@ -287,6 +309,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
|||
|
||||
if(media.isCurrentlyPlaying()) {
|
||||
container.setBackgroundColor(playingBackGroundColor);
|
||||
currentlyPlayingItem = this;
|
||||
} else {
|
||||
container.setBackgroundColor(normalBackGroundColor);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.lang.ref.WeakReference;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
|
||||
public class RatingDialog {
|
||||
|
||||
|
@ -59,14 +60,10 @@ public class RatingDialog {
|
|||
|
||||
private static void rateNow() {
|
||||
Context context = mContext.get();
|
||||
if(context == null) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
final String appPackage = "de.danoeh.antennapod";
|
||||
final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod");
|
||||
saveRated();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
||||
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
@ -39,17 +40,16 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
|
|||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
import de.danoeh.antennapod.core.event.FeedItemEvent;
|
||||
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.service.download.DownloadRequest;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.view.EmptyViewHandler;
|
||||
|
@ -197,10 +197,7 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||
if (!super.onOptionsItemSelected(item)) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.refresh_item:
|
||||
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
||||
if (feeds != null) {
|
||||
DBTasks.refreshAllFeeds(getActivity(), feeds);
|
||||
}
|
||||
AutoUpdateManager.runImmediate(requireContext());
|
||||
return true;
|
||||
case R.id.mark_all_read_item:
|
||||
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
|
||||
|
@ -388,6 +385,13 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (listAdapter != null) {
|
||||
listAdapter.notifyCurrentlyPlayingItemChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
|
||||
return true;
|
||||
}
|
||||
|
@ -397,7 +401,7 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
DownloaderUpdate update = event.update;
|
||||
downloaderList = update.downloaders;
|
||||
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
|
||||
if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
||||
requireActivity().invalidateOptionsMenu();
|
||||
}
|
||||
if (update.mediaIds.length > 0) {
|
||||
|
|
|
@ -18,7 +18,7 @@ import com.bumptech.glide.Glide;
|
|||
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.event.ServiceEvent;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
|
@ -28,6 +28,9 @@ import io.reactivex.Maybe;
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
/**
|
||||
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
|
||||
|
@ -138,6 +141,7 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
controller = setupPlaybackController();
|
||||
controller.init();
|
||||
loadMediaInfo();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -147,6 +151,12 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
controller.release();
|
||||
controller = null;
|
||||
}
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
onPositionObserverUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
package de.danoeh.antennapod.fragment;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.LightingColorFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.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 de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.LangUtils;
|
||||
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
|
||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.MaybeOnSubscribe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
/**
|
||||
* Displays information about a feed.
|
||||
*/
|
||||
public class FeedInfoFragment extends Fragment {
|
||||
|
||||
private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
|
||||
private static final String TAG = "FeedInfoActivity";
|
||||
|
||||
private Feed feed;
|
||||
private Disposable disposable;
|
||||
private ImageView imgvCover;
|
||||
private TextView txtvTitle;
|
||||
private TextView txtvDescription;
|
||||
private TextView lblLanguage;
|
||||
private TextView txtvLanguage;
|
||||
private TextView lblAuthor;
|
||||
private TextView txtvAuthor;
|
||||
private TextView txtvUrl;
|
||||
private TextView txtvAuthorHeader;
|
||||
private ImageView imgvBackground;
|
||||
|
||||
public static FeedInfoFragment newInstance(Feed feed) {
|
||||
FeedInfoFragment fragment = new FeedInfoFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putLong(EXTRA_FEED_ID, feed.getId());
|
||||
fragment.setArguments(arguments);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
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) getContext()
|
||||
.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cm.setPrimaryClip(clipData);
|
||||
Toast t = Toast.makeText(getContext(), R.string.copied_url_msg, Toast.LENGTH_SHORT);
|
||||
t.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
((MainActivity)getActivity()).getSupportActionBar().setTitle(R.string.feed_info_label);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.feedinfo, null);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
imgvCover = root.findViewById(R.id.imgvCover);
|
||||
txtvTitle = root.findViewById(R.id.txtvTitle);
|
||||
txtvAuthorHeader = root.findViewById(R.id.txtvAuthor);
|
||||
imgvBackground = root.findViewById(R.id.imgvBackground);
|
||||
root.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
|
||||
root.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
|
||||
// https://github.com/bumptech/glide/issues/529
|
||||
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
|
||||
|
||||
|
||||
txtvDescription = root.findViewById(R.id.txtvDescription);
|
||||
lblLanguage = root.findViewById(R.id.lblLanguage);
|
||||
txtvLanguage = root.findViewById(R.id.txtvLanguage);
|
||||
lblAuthor = root.findViewById(R.id.lblAuthor);
|
||||
txtvAuthor = root.findViewById(R.id.txtvDetailsAuthor);
|
||||
txtvUrl = root.findViewById(R.id.txtvUrl);
|
||||
|
||||
txtvUrl.setOnClickListener(copyUrlToClipboard);
|
||||
postponeEnterTransition();
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
long feedId = getArguments().getLong(EXTRA_FEED_ID);
|
||||
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 -> {
|
||||
feed = result;
|
||||
showFeed();
|
||||
}, error -> Log.d(TAG, Log.getStackTraceString(error)),
|
||||
this::startPostponedEnterTransition);
|
||||
}
|
||||
|
||||
private void showFeed() {
|
||||
Log.d(TAG, "Language is " + feed.getLanguage());
|
||||
Log.d(TAG, "Author is " + feed.getAuthor());
|
||||
Log.d(TAG, "URL is " + feed.getDownload_url());
|
||||
Glide.with(getContext())
|
||||
.load(feed.getImageLocation())
|
||||
.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(getContext())
|
||||
.load(feed.getImageLocation())
|
||||
.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());
|
||||
|
||||
String description = feed.getDescription();
|
||||
if(description != null) {
|
||||
if(Feed.TYPE_ATOM1.equals(feed.getType())) {
|
||||
HtmlToPlainText formatter = new HtmlToPlainText();
|
||||
Document feedDescription = Jsoup.parse(feed.getDescription());
|
||||
description = StringUtils.trim(formatter.getPlainText(feedDescription));
|
||||
}
|
||||
} else {
|
||||
description = "";
|
||||
}
|
||||
txtvDescription.setText(description);
|
||||
|
||||
if (!TextUtils.isEmpty(feed.getAuthor())) {
|
||||
txtvAuthor.setText(feed.getAuthor());
|
||||
txtvAuthorHeader.setText(feed.getAuthor());
|
||||
} else {
|
||||
lblAuthor.setVisibility(View.GONE);
|
||||
txtvAuthor.setVisibility(View.GONE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(feed.getLanguage())) {
|
||||
txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage()));
|
||||
} else {
|
||||
lblLanguage.setVisibility(View.GONE);
|
||||
txtvLanguage.setVisibility(View.GONE);
|
||||
}
|
||||
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
|
||||
Iconify.addIcons(txtvUrl);
|
||||
|
||||
getActivity().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.feedinfo, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
|
||||
menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
|
||||
IntentUtils.isCallable(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
boolean handled = false;
|
||||
try {
|
||||
handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getContext(), e.getMessage());
|
||||
}
|
||||
return handled || super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import com.bumptech.glide.request.RequestOptions;
|
|||
import com.joanzapata.iconify.Iconify;
|
||||
import com.joanzapata.iconify.widget.IconTextView;
|
||||
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
|
@ -37,8 +38,6 @@ import org.greenrobot.eventbus.ThreadMode;
|
|||
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.FeedItemlistAdapter;
|
||||
import de.danoeh.antennapod.core.asynctask.FeedRemover;
|
||||
|
@ -140,37 +139,33 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
public void onHiddenChanged(boolean hidden) {
|
||||
super.onHiddenChanged(hidden);
|
||||
if (!hidden && getActivity() != null) {
|
||||
((MainActivity) getActivity()).getSupportActionBar().setTitle("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
registerForContextMenu(getListView());
|
||||
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
((MainActivity)getActivity()).getSupportActionBar().setTitle("");
|
||||
updateProgressBarVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
if(disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
resetViewState();
|
||||
}
|
||||
|
||||
private void resetViewState() {
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
adapter = null;
|
||||
listFooter = null;
|
||||
}
|
||||
|
@ -343,13 +338,6 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
registerForContextMenu(getListView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
if(adapter == null) {
|
||||
|
@ -390,14 +378,21 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
DownloaderUpdate update = event.update;
|
||||
downloaderList = update.downloaders;
|
||||
if (isUpdatingFeed != event.update.feedIds.length > 0) {
|
||||
if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) {
|
||||
updateProgressBarVisibility();
|
||||
}
|
||||
if(adapter != null && update.mediaIds.length > 0) {
|
||||
if (adapter != null && update.mediaIds.length > 0) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (adapter != null) {
|
||||
adapter.notifyCurrentlyPlayingItemChanged(event, getListView());
|
||||
}
|
||||
}
|
||||
|
||||
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||
|
||||
@Override
|
||||
|
@ -421,8 +416,9 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
|
||||
}
|
||||
|
||||
private void onFragmentLoaded() {
|
||||
if(!isVisible()) {
|
||||
private void displayList() {
|
||||
if (getView() == null) {
|
||||
Log.e(TAG, "Required root view is not yet created. Stop binding data to UI.");
|
||||
return;
|
||||
}
|
||||
if (adapter == null) {
|
||||
|
@ -506,10 +502,8 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
imgvCover.setOnClickListener(v -> showFeedInfo());
|
||||
butShowSettings.setOnClickListener(v -> {
|
||||
if (feed != null) {
|
||||
Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class);
|
||||
startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID,
|
||||
feed.getId());
|
||||
startActivity(startIntent);
|
||||
FeedSettingsFragment fragment = FeedSettingsFragment.newInstance(feed);
|
||||
((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP);
|
||||
}
|
||||
});
|
||||
headerCreated = true;
|
||||
|
@ -517,10 +511,8 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
|
||||
private void showFeedInfo() {
|
||||
if (feed != null) {
|
||||
Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
|
||||
startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
|
||||
feed.getId());
|
||||
startActivity(startIntent);
|
||||
FeedInfoFragment fragment = FeedInfoFragment.newInstance(feed);
|
||||
((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,7 +618,7 @@ public class FeedItemlistFragment extends ListFragment {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
feed = result.orElse(null);
|
||||
onFragmentLoaded();
|
||||
displayList();
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,39 +1,66 @@
|
|||
package de.danoeh.antennapod.fragment;
|
||||
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.util.Log;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedFilter;
|
||||
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||
import de.danoeh.antennapod.dialog.EpisodeFilterDialog;
|
||||
import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
|
||||
|
||||
import static de.danoeh.antennapod.activity.FeedSettingsActivity.EXTRA_FEED_ID;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.MaybeOnSubscribe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class FeedSettingsFragment extends PreferenceFragmentCompat {
|
||||
private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter";
|
||||
private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
|
||||
private static final String TAG = "FeedSettingsFragment";
|
||||
|
||||
private Feed feed;
|
||||
private Disposable disposable;
|
||||
private FeedPreferences feedPreferences;
|
||||
|
||||
public static FeedSettingsFragment newInstance(Feed feed) {
|
||||
FeedSettingsFragment fragment = new FeedSettingsFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putLong(EXTRA_FEED_ID, feed.getId());
|
||||
fragment.setArguments(arguments);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.feed_settings);
|
||||
|
||||
postponeEnterTransition();
|
||||
long feedId = getArguments().getLong(EXTRA_FEED_ID);
|
||||
ViewModelProviders.of(getActivity()).get(FeedSettingsViewModel.class).getFeed(feedId)
|
||||
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 -> {
|
||||
feed = result;
|
||||
feedPreferences = feed.getPreferences();
|
||||
((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle());
|
||||
|
||||
setupAutoDownloadPreference();
|
||||
setupKeepUpdatedPreference();
|
||||
|
@ -43,7 +70,31 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
|
|||
|
||||
updateAutoDeleteSummary();
|
||||
updateAutoDownloadEnabled();
|
||||
}).dispose();
|
||||
}, error -> Log.d(TAG, Log.getStackTraceString(error)),
|
||||
this::startPostponedEnterTransition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.feed_settings_label);
|
||||
if (feed != null) {
|
||||
((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
((MainActivity) getActivity()).getSupportActionBar().setSubtitle(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupEpisodeFilterPreference() {
|
||||
|
|
|
@ -96,13 +96,7 @@ public class ItemDescriptionFragment extends Fragment {
|
|||
if (Timeline.isTimecodeLink(url)) {
|
||||
onTimecodeLinkSelected(url);
|
||||
} else {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
IntentUtils.openInBrowser(getContext(), url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -159,11 +153,7 @@ public class ItemDescriptionFragment extends Fragment {
|
|||
if (selectedURL != null) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.open_in_browser_item:
|
||||
Uri uri = Uri.parse(selectedURL);
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
if(IntentUtils.isCallable(getActivity(), intent)) {
|
||||
getActivity().startActivity(intent);
|
||||
}
|
||||
IntentUtils.openInBrowser(getContext(), selectedURL);
|
||||
break;
|
||||
case R.id.share_url_item:
|
||||
ShareUtils.shareLink(getActivity(), selectedURL);
|
||||
|
|
|
@ -210,10 +210,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
|
|||
webvDescription.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
if(IntentUtils.isCallable(getActivity(), intent)) {
|
||||
startActivity(intent);
|
||||
}
|
||||
IntentUtils.openInBrowser(getContext(), url);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -484,11 +481,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
|
|||
if (selectedURL != null) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.open_in_browser_item:
|
||||
Uri uri = Uri.parse(selectedURL);
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
if(IntentUtils.isCallable(getActivity(), intent)) {
|
||||
getActivity().startActivity(intent);
|
||||
}
|
||||
IntentUtils.openInBrowser(getContext(), selectedURL);
|
||||
break;
|
||||
case R.id.share_url_item:
|
||||
ShareUtils.shareLink(getActivity(), selectedURL);
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.widget.TextView;
|
|||
|
||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
||||
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
@ -41,14 +42,12 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
|||
import de.danoeh.antennapod.core.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.core.event.QueueEvent;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.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.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
|
@ -56,6 +55,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
|
|||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.QueueSorter;
|
||||
import de.danoeh.antennapod.core.util.SortOrder;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
|
@ -201,8 +201,8 @@ public class QueueFragment extends Fragment {
|
|||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
DownloaderUpdate update = event.update;
|
||||
downloaderList = update.downloaders;
|
||||
if (isUpdatingFeeds != update.feedIds.length > 0) {
|
||||
getActivity().supportInvalidateOptionsMenu();
|
||||
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
||||
getActivity().invalidateOptionsMenu();
|
||||
}
|
||||
if (recyclerAdapter != null && update.mediaIds.length > 0) {
|
||||
for (long mediaId : update.mediaIds) {
|
||||
|
@ -214,6 +214,13 @@ public class QueueFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||
if (recyclerAdapter != null) {
|
||||
recyclerAdapter.notifyCurrentlyPlayingItemChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveScrollPosition() {
|
||||
int firstItem = layoutManager.findFirstVisibleItemPosition();
|
||||
View firstItemView = layoutManager.findViewByPosition(firstItem);
|
||||
|
@ -305,10 +312,7 @@ public class QueueFragment extends Fragment {
|
|||
toggleQueueLock();
|
||||
return true;
|
||||
case R.id.refresh_item:
|
||||
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
||||
if (feeds != null) {
|
||||
DBTasks.refreshAllFeeds(getActivity(), feeds);
|
||||
}
|
||||
AutoUpdateManager.runImmediate(requireContext());
|
||||
return true;
|
||||
case R.id.clear_queue:
|
||||
// make sure the user really wants to clear the queue
|
||||
|
|
|
@ -25,19 +25,28 @@ import de.danoeh.antennapod.activity.MainActivity;
|
|||
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
|
||||
import de.danoeh.antennapod.core.asynctask.FeedRemover;
|
||||
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
|
||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||
import de.danoeh.antennapod.core.event.DownloaderUpdate;
|
||||
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.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
/**
|
||||
* Fragment for displaying feed subscriptions
|
||||
|
@ -56,6 +65,7 @@ public class SubscriptionFragment extends Fragment {
|
|||
private SubscriptionsAdapter subscriptionAdapter;
|
||||
|
||||
private int mPosition = -1;
|
||||
private boolean isUpdatingFeeds = false;
|
||||
|
||||
private Disposable disposable;
|
||||
private SharedPreferences prefs;
|
||||
|
@ -89,6 +99,8 @@ public class SubscriptionFragment extends Fragment {
|
|||
menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3);
|
||||
menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4);
|
||||
menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5);
|
||||
|
||||
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,6 +109,9 @@ public class SubscriptionFragment extends Fragment {
|
|||
return true;
|
||||
}
|
||||
switch (item.getItemId()) {
|
||||
case R.id.refresh_item:
|
||||
AutoUpdateManager.runImmediate(requireContext());
|
||||
return true;
|
||||
case R.id.subscription_num_columns_2:
|
||||
setColumnNumber(2);
|
||||
return true;
|
||||
|
@ -136,6 +151,7 @@ public class SubscriptionFragment extends Fragment {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
loadSubscriptions();
|
||||
}
|
||||
|
||||
|
@ -143,6 +159,7 @@ public class SubscriptionFragment extends Fragment {
|
|||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
if(disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
|
@ -278,6 +295,17 @@ public class SubscriptionFragment extends Fragment {
|
|||
}
|
||||
};
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DownloadEvent event) {
|
||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
||||
getActivity().invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||
|
||||
private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
|
||||
@Override
|
||||
public int getCount() {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package de.danoeh.antennapod.fragment;
|
||||
|
||||
public enum TransitionEffect {
|
||||
NONE, FLIP, FADE
|
||||
}
|
|
@ -1,29 +1,42 @@
|
|||
package de.danoeh.antennapod.fragment.preferences;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.preference.CheckBoxPreference;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.util.Log;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
|
||||
public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
|
||||
private static final String TAG = "AutoDnldPrefFragment";
|
||||
|
||||
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
|
||||
private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt";
|
||||
|
||||
private CheckBoxPreference[] selectedNetworks;
|
||||
|
||||
private Preference prefPermissionRequestPromptOnAndroid10 = null;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.preferences_autodownload);
|
||||
|
@ -34,6 +47,12 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
|
|||
buildEpisodeCleanupPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
@ -175,10 +194,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
|
||||
private void setSelectedNetworksEnabled(boolean b) {
|
||||
if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedNetworks != null) {
|
||||
for (Preference p : selectedNetworks) {
|
||||
p.setEnabled(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
|
||||
return;
|
||||
}
|
||||
if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) &&
|
||||
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
buildAutodownloadSelectedNetworksPreference();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cases Android 10(Q) or later
|
||||
if (prefPermissionRequestPromptOnAndroid10 != null) {
|
||||
getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10);
|
||||
prefPermissionRequestPromptOnAndroid10 = null;
|
||||
}
|
||||
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Case location permission not yet granted, permission-specific UI is needed
|
||||
if (!wifiFilterEnabled) {
|
||||
// Don't show the UI when WiFi filter disabled.
|
||||
// it still return true, so that the caller knows
|
||||
// it does not have required permission, and will not invoke codes that require so.
|
||||
return true;
|
||||
}
|
||||
|
||||
Preference pref = new Preference(requireActivity());
|
||||
pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT);
|
||||
pref.setTitle(R.string.autodl_wifi_filter_permission_title);
|
||||
pref.setSummary(R.string.autodl_wifi_filter_permission_message);
|
||||
pref.setIcon(R.drawable.ic_warning_red);
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
|
||||
return true;
|
||||
});
|
||||
pref.setPersistent(false);
|
||||
getPreferenceScreen().addPreference(pref);
|
||||
prefPermissionRequestPromptOnAndroid10 = pref;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.text.Html;
|
|||
import android.text.format.DateUtils;
|
||||
import android.widget.Toast;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.service.GpodnetSyncService;
|
||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||
|
@ -29,6 +30,12 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupGpodderScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
@ -14,6 +14,12 @@ public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupIntegrationsScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.integrations_label);
|
||||
}
|
||||
|
||||
private void setupIntegrationsScreen() {
|
||||
findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
|
||||
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
package de.danoeh.antennapod.fragment.preferences;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
|
||||
import com.bytehamster.lib.preferencesearch.SearchPreference;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.AboutActivity;
|
||||
import de.danoeh.antennapod.activity.BugReportActivity;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.activity.StatisticsActivity;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
|
||||
public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
||||
private static final String TAG = "MainPreferencesFragment";
|
||||
|
@ -37,6 +33,12 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label);
|
||||
}
|
||||
|
||||
private void setupMainScreen() {
|
||||
findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
|
||||
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface);
|
||||
|
@ -67,16 +69,17 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
|||
);
|
||||
findPreference(STATISTICS).setOnPreferenceClickListener(
|
||||
preference -> {
|
||||
startActivity(new Intent(getActivity(), StatisticsActivity.class));
|
||||
getFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment())
|
||||
.addToBackStack(getString(R.string.statistics_label)).commit();
|
||||
return true;
|
||||
}
|
||||
);
|
||||
findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
|
||||
openInBrowser("https://antennapod.org/faq.html");
|
||||
IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html");
|
||||
return true;
|
||||
});
|
||||
findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> {
|
||||
openInBrowser("https://groups.google.com/forum/#!forum/antennapod");
|
||||
IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod");
|
||||
return true;
|
||||
});
|
||||
findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
|
||||
|
@ -85,16 +88,6 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
|
|||
});
|
||||
}
|
||||
|
||||
private void openInBrowser(String url) {
|
||||
try {
|
||||
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(myIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
|
||||
SearchConfiguration config = searchPreference.getSearchConfiguration();
|
||||
|
|
|
@ -30,6 +30,12 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupNetworkScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.network_pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.support.v7.preference.ListPreference;
|
|||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MediaplayerActivity;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
|
||||
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
|
||||
|
@ -27,6 +28,12 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
|
|||
buildSmartMarkAsPlayedPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
package de.danoeh.antennapod.activity;
|
||||
package de.danoeh.antennapod.fragment.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
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 io.reactivex.Observable;
|
||||
|
@ -28,10 +32,8 @@ import io.reactivex.schedulers.Schedulers;
|
|||
/**
|
||||
* Displays the 'statistics' screen
|
||||
*/
|
||||
public class StatisticsActivity extends AppCompatActivity
|
||||
implements AdapterView.OnItemClickListener {
|
||||
|
||||
private static final String TAG = StatisticsActivity.class.getSimpleName();
|
||||
public class StatisticsFragment extends Fragment implements AdapterView.OnItemClickListener {
|
||||
private static final String TAG = StatisticsFragment.class.getSimpleName();
|
||||
private static final String PREF_NAME = "StatisticsActivityPrefs";
|
||||
private static final String PREF_COUNT_ALL = "countAll";
|
||||
|
||||
|
@ -44,54 +46,51 @@ public class StatisticsActivity extends AppCompatActivity
|
|||
private SharedPreferences prefs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(UserPreferences.getTheme());
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
setContentView(R.layout.statistics_activity);
|
||||
|
||||
prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
totalTimeTextView = findViewById(R.id.total_time);
|
||||
feedStatisticsList = findViewById(R.id.statistics_list);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
listAdapter = new StatisticsListAdapter(this);
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.statistics_activity, container, false);
|
||||
totalTimeTextView = root.findViewById(R.id.total_time);
|
||||
feedStatisticsList = root.findViewById(R.id.statistics_list);
|
||||
progressBar = root.findViewById(R.id.progressBar);
|
||||
listAdapter = new StatisticsListAdapter(getContext());
|
||||
listAdapter.setCountAll(countAll);
|
||||
feedStatisticsList.setAdapter(listAdapter);
|
||||
feedStatisticsList.setOnItemClickListener(this);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label);
|
||||
refreshStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.statistics, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.statistics_mode) {
|
||||
if (item.getItemId() == R.id.statistics_mode) {
|
||||
selectStatisticsMode();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void selectStatisticsMode() {
|
||||
View contentView = View.inflate(this, R.layout.statistics_mode_select_dialog, null);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
View contentView = View.inflate(getContext(), R.layout.statistics_mode_select_dialog, null);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
builder.setView(contentView);
|
||||
builder.setTitle(R.string.statistics_mode);
|
||||
|
||||
|
@ -126,8 +125,8 @@ public class StatisticsActivity extends AppCompatActivity
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
totalTimeTextView.setText(Converter
|
||||
.shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
|
||||
totalTimeTextView.setText(Converter.shortLocalizedDuration(getContext(),
|
||||
countAll ? result.totalTimeCountAll : result.totalTime));
|
||||
listAdapter.update(result.feedTime);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
totalTimeTextView.setVisibility(View.VISIBLE);
|
||||
|
@ -139,14 +138,13 @@ public class StatisticsActivity extends AppCompatActivity
|
|||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
DBReader.StatisticsItem stats = listAdapter.getItem(position);
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
|
||||
dialog.setTitle(stats.feed.getTitle());
|
||||
dialog.setMessage(getString(R.string.statistics_details_dialog,
|
||||
countAll ? stats.episodesStartedIncludingMarked : stats.episodesStarted,
|
||||
stats.episodes,
|
||||
Converter.shortLocalizedDuration(this, countAll ?
|
||||
stats.timePlayedCountAll : stats.timePlayed),
|
||||
Converter.shortLocalizedDuration(this, stats.time)));
|
||||
stats.episodes, Converter.shortLocalizedDuration(getContext(),
|
||||
countAll ? stats.timePlayedCountAll : stats.timePlayed),
|
||||
Converter.shortLocalizedDuration(getContext(), stats.time)));
|
||||
dialog.setPositiveButton(android.R.string.ok, null);
|
||||
dialog.show();
|
||||
}
|
|
@ -23,6 +23,7 @@ import de.danoeh.antennapod.R;
|
|||
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
|
||||
import de.danoeh.antennapod.activity.ImportExportActivity;
|
||||
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
|
||||
import de.danoeh.antennapod.asynctask.ExportWorker;
|
||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
||||
|
@ -63,6 +64,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupStorageScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.storage_pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.widget.ListView;
|
|||
import android.widget.Toast;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
|
@ -25,6 +26,12 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
|
|||
setupInterfaceScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.user_interface_label);
|
||||
}
|
||||
|
||||
private void setupInterfaceScreen() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
|
|
|
@ -214,14 +214,7 @@ public class FeedItemMenuHandler {
|
|||
DBWriter.setFeedItemAutoDownload(selectedItem, false);
|
||||
break;
|
||||
case R.id.visit_website_item:
|
||||
Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
if(IntentUtils.isCallable(context, intent)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem));
|
||||
break;
|
||||
case R.id.share_link_item:
|
||||
ShareUtils.shareFeedItemLink(context, selectedItem);
|
||||
|
|
|
@ -86,14 +86,7 @@ public class FeedMenuHandler {
|
|||
conDialog.createNewDialog().show();
|
||||
break;
|
||||
case R.id.visit_website_item:
|
||||
Uri uri = Uri.parse(selectedFeed.getLink());
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
if(IntentUtils.isCallable(context, intent)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
IntentUtils.openInBrowser(context, selectedFeed.getLink());
|
||||
break;
|
||||
case R.id.share_link_item:
|
||||
ShareUtils.shareFeedlink(context, selectedFeed);
|
||||
|
|
|
@ -3,9 +3,11 @@ package de.danoeh.antennapod.preferences;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
||||
|
||||
public class PreferenceUpgrader {
|
||||
|
@ -22,7 +24,7 @@ public class PreferenceUpgrader {
|
|||
|
||||
if (oldVersion != newVersion) {
|
||||
NotificationUtils.createChannels(context);
|
||||
UserPreferences.restartUpdateAlarm();
|
||||
AutoUpdateManager.restartUpdateAlarm();
|
||||
|
||||
upgrade(oldVersion);
|
||||
upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package de.danoeh.antennapod.viewmodel;
|
||||
|
||||
import android.arch.lifecycle.ViewModel;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import io.reactivex.Maybe;
|
||||
|
||||
public class FeedSettingsViewModel extends ViewModel {
|
||||
private Feed feed;
|
||||
|
||||
public Maybe<Feed> getFeed(long feedId) {
|
||||
if (feed == null) {
|
||||
return loadFeed(feedId);
|
||||
} else {
|
||||
return Maybe.just(feed);
|
||||
}
|
||||
}
|
||||
|
||||
private Maybe<Feed> loadFeed(long feedId) {
|
||||
return Maybe.create(emitter -> {
|
||||
Feed feed = DBReader.getFeed(feedId);
|
||||
if (feed != null) {
|
||||
this.feed = feed;
|
||||
emitter.onSuccess(feed);
|
||||
} else {
|
||||
emitter.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Before rotating, immediately set the alpha to 0. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0"
|
||||
android:propertyName="alpha"
|
||||
android:duration="0" />
|
||||
|
||||
<!-- Rotate. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="-180"
|
||||
android:valueTo="0"
|
||||
android:propertyName="rotationY"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:duration="@integer/card_flip_time_full" />
|
||||
|
||||
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/card_flip_time_half"
|
||||
android:duration="1" />
|
||||
</set>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Rotate. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="0"
|
||||
android:valueTo="180"
|
||||
android:propertyName="rotationY"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:duration="@integer/card_flip_time_full" />
|
||||
|
||||
<!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/card_flip_time_half"
|
||||
android:duration="1" />
|
||||
</set>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Before rotating, immediately set the alpha to 0. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0"
|
||||
android:propertyName="alpha"
|
||||
android:duration="0" />
|
||||
|
||||
<!-- Rotate. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="180"
|
||||
android:valueTo="0"
|
||||
android:propertyName="rotationY"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:duration="@integer/card_flip_time_full" />
|
||||
|
||||
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/card_flip_time_half"
|
||||
android:duration="1" />
|
||||
</set>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Rotate. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-180"
|
||||
android:propertyName="rotationY"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:duration="@integer/card_flip_time_full" />
|
||||
|
||||
<!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
|
||||
<objectAnimator
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/card_flip_time_half"
|
||||
android:duration="1" />
|
||||
</set>
|
|
@ -1,41 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_time_listened_to_podcasts"
|
||||
android:gravity="center_horizontal"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_gravity="center_horizontal"
|
||||
style="?android:attr/progressBarStyleLarge"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/total_time_listened_to_podcasts"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/total_time"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="45sp"/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/total_time"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="28sp"
|
||||
tools:text="10.0 hours"/>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_gravity="center_horizontal"
|
||||
style="?android:attr/progressBarStyleSmall"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/dividerVertical"/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/statistics_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:choiceMode="singleChoice"
|
||||
android:clipToPadding="false"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:scrollbarStyle="outsideOverlay" />
|
||||
android:id="@+id/statistics_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:choiceMode="singleChoice"
|
||||
android:clipToPadding="false"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
tools:listitem="@layout/statistics_listitem"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -3,63 +3,54 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/listitem_iconwithtext_height"
|
||||
android:paddingLeft="@dimen/listitem_threeline_verticalpadding"
|
||||
android:paddingStart="@dimen/listitem_threeline_verticalpadding"
|
||||
android:paddingRight="@dimen/listitem_threeline_verticalpadding"
|
||||
android:paddingEnd="@dimen/listitem_threeline_verticalpadding"
|
||||
tools:background="@android:color/darker_gray">
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgvCover"
|
||||
android:contentDescription="@string/cover_label"
|
||||
android:layout_width="@dimen/thumbnail_length_navlist"
|
||||
android:layout_height="@dimen/thumbnail_length_navlist"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:cropToPadding="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
tools:src="@drawable/ic_antenna"
|
||||
tools:background="@android:color/holo_green_dark"/>
|
||||
android:id="@+id/imgvCover"
|
||||
android:contentDescription="@string/cover_label"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:cropToPadding="true"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/ic_antenna"
|
||||
tools:background="@android:color/holo_green_dark"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/list_vertical_padding"
|
||||
android:layout_marginStart="@dimen/list_vertical_padding"
|
||||
android:lines="1"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
android:textSize="@dimen/text_size_navdrawer"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
tools:text="23"
|
||||
tools:background="@android:color/holo_green_dark"/>
|
||||
android:id="@+id/txtvTitle"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_toRightOf="@id/imgvCover"
|
||||
android:layout_alignTop="@id/imgvCover"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
tools:text="Feed title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvTitle"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/text_size_navdrawer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
|
||||
android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
|
||||
android:layout_toRightOf="@id/imgvCover"
|
||||
android:layout_toEndOf="@id/imgvCover"
|
||||
android:layout_toLeftOf="@id/txtvTime"
|
||||
android:layout_toStartOf="@id/txtvTime"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
tools:text="Navigation feed item title"
|
||||
tools:background="@android:color/holo_green_dark"/>
|
||||
|
||||
android:id="@+id/txtvTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
android:textSize="14sp"
|
||||
android:layout_toEndOf="@+id/imgvCover"
|
||||
android:layout_toRightOf="@+id/imgvCover"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_below="@+id/txtvTitle"
|
||||
tools:text="23 hours"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="always"
|
||||
android:icon="?attr/navigation_refresh"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/subscription_num_columns"
|
||||
android:title="@string/subscription_num_columns"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="card_flip_time_full">400</integer>
|
||||
<integer name="card_flip_time_half">200</integer>
|
||||
</resources>
|
|
@ -29,17 +29,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
public static final String TAG = "CastEnabledActivity";
|
||||
|
||||
protected CastManager castManager;
|
||||
protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
|
||||
private CastConsumer castConsumer;
|
||||
private CastManager castManager;
|
||||
|
||||
private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
|
||||
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
|
||||
registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
castConsumer = new DefaultCastConsumer() {
|
||||
@Override
|
||||
public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
|
||||
onCastConnectionChanged(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
onCastConnectionChanged(false);
|
||||
}
|
||||
};
|
||||
castManager = CastManager.getInstance();
|
||||
castManager.addCastConsumer(castConsumer);
|
||||
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
|
||||
|
@ -48,6 +65,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (!CastManager.isInitialized()) {
|
||||
super.onDestroy();
|
||||
return;
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
castManager.removeCastConsumer(castConsumer);
|
||||
|
@ -58,6 +79,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
@CallSuper
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
if (!CastManager.isInitialized()) {
|
||||
return true;
|
||||
}
|
||||
getMenuInflater().inflate(R.menu.cast_enabled, menu);
|
||||
castButtonVisibilityManager.setMenu(menu);
|
||||
return true;
|
||||
|
@ -67,6 +91,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
@CallSuper
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
if (!CastManager.isInitialized()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
|
||||
if (mediaRouteButton == null) {
|
||||
Log.wtf(TAG, "MediaRoute item could not be found on the menu!", new Exception());
|
||||
|
@ -83,15 +111,22 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
castButtonVisibilityManager.setResumed(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
castButtonVisibilityManager.setResumed(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
|
||||
|
@ -105,18 +140,6 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
CastConsumer castConsumer = new DefaultCastConsumer() {
|
||||
@Override
|
||||
public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
|
||||
onCastConnectionChanged(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
onCastConnectionChanged(false);
|
||||
}
|
||||
};
|
||||
|
||||
private void onCastConnectionChanged(boolean connected) {
|
||||
if (connected) {
|
||||
castButtonVisibilityManager.onConnected();
|
||||
|
@ -133,6 +156,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
|
|||
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
|
||||
*/
|
||||
public final void requestCastButton(int showAsAction) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
castButtonVisibilityManager.requestCastButton(showAsAction);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package de.danoeh.antennapod.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
|
||||
|
||||
|
@ -18,6 +23,7 @@ public class PreferenceControllerFlavorHelper {
|
|||
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
|
||||
.isGooglePlayServicesAvailable(ui.getActivity());
|
||||
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
|
||||
displayRestartRequiredDialog(ui.requireContext());
|
||||
return true;
|
||||
} else {
|
||||
GoogleApiAvailability.getInstance()
|
||||
|
@ -29,4 +35,13 @@ public class PreferenceControllerFlavorHelper {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static void displayRestartRequiredDialog(@NonNull Context context) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(android.R.string.dialog_alert_title);
|
||||
dialog.setMessage(R.string.pref_restart_required);
|
||||
dialog.setPositiveButton(android.R.string.ok, null);
|
||||
dialog.setCancelable(false);
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ project.ext {
|
|||
targetSdkVersion = 28
|
||||
|
||||
supportVersion = "27.1.1"
|
||||
lifecycle_version = "1.1.1"
|
||||
workManagerVersion = "1.0.1"
|
||||
awaitilityVersion = "3.1.2"
|
||||
commonsioVersion = "2.5"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<property name="fileExtensions" value="java, xml"/>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
|
@ -17,12 +18,65 @@
|
|||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="CatchParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="InterfaceTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
|
|
|
@ -76,7 +76,6 @@ dependencies {
|
|||
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
|
||||
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
|
||||
implementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||
|
||||
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerVersion"
|
||||
implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
|
||||
|
@ -92,6 +91,7 @@ dependencies {
|
|||
System.out.println("core: free build hack, skipping some dependencies")
|
||||
}
|
||||
|
||||
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:1.10.19'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
|
|
|
@ -25,4 +25,8 @@ public class DownloadEvent {
|
|||
"update=" + update +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean hasChangedFeedUpdateStatus(boolean oldStatus) {
|
||||
return oldStatus != update.feedIds.length > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package de.danoeh.antennapod.core.event;
|
||||
|
||||
public class PlaybackPositionEvent {
|
||||
private final int position;
|
||||
private final int duration;
|
||||
|
||||
public PlaybackPositionEvent(int position, int duration) {
|
||||
this.position = position;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package de.danoeh.antennapod.core.event;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class ProgressEvent {
|
||||
|
||||
public enum Action {
|
||||
START, END
|
||||
}
|
||||
|
||||
public final Action action;
|
||||
public final String message;
|
||||
|
||||
private ProgressEvent(Action action, String message) {
|
||||
this.action = action;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static ProgressEvent start(String message) {
|
||||
return new ProgressEvent(Action.START, message);
|
||||
}
|
||||
|
||||
public static ProgressEvent end() {
|
||||
return new ProgressEvent(Action.END, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("action", action)
|
||||
.append("message", message)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -218,6 +218,8 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, ImageR
|
|||
return itemIdentifier;
|
||||
} else if (title != null && !title.isEmpty()) {
|
||||
return title;
|
||||
} else if (hasMedia() && media.getDownload_url() != null) {
|
||||
return media.getDownload_url();
|
||||
} else {
|
||||
return link;
|
||||
}
|
||||
|
|
|
@ -92,11 +92,9 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
|
|||
@Nullable
|
||||
@Override
|
||||
public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
|
||||
Log.d(TAG, "buildLoadData() called with: " + "model = [" + model + "], width = ["
|
||||
+ width + "], height = [" + height + "]");
|
||||
if(TextUtils.isEmpty(model)) {
|
||||
if (TextUtils.isEmpty(model)) {
|
||||
return null;
|
||||
} else if(model.startsWith("/")) {
|
||||
} else if (model.startsWith("/")) {
|
||||
return new LoadData<>(new ObjectKey(model), new AudioCoverFetcher(model));
|
||||
} else {
|
||||
GlideUrl url = new GlideUrl(model);
|
||||
|
|
|
@ -4,7 +4,12 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import android.util.Log;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
|
||||
/**
|
||||
* Provides access to preferences set by the playback service. A private
|
||||
|
@ -19,35 +24,35 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
|
|||
* Contains the feed id of the currently playing item if it is a FeedMedia
|
||||
* object.
|
||||
*/
|
||||
public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
|
||||
private static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
|
||||
|
||||
/**
|
||||
* Contains the id of the currently playing FeedMedia object or
|
||||
* NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object.
|
||||
*/
|
||||
public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
|
||||
private static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
|
||||
|
||||
/**
|
||||
* Type of the media object that is currently being played. This preference
|
||||
* is set to NO_MEDIA_PLAYING after playback has been completed and is set
|
||||
* as soon as the 'play' button is pressed.
|
||||
*/
|
||||
public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
|
||||
private static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
|
||||
|
||||
/**
|
||||
* True if last played media was streamed.
|
||||
*/
|
||||
public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
|
||||
private static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
|
||||
|
||||
/**
|
||||
* True if last played media was a video.
|
||||
*/
|
||||
public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
|
||||
private static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
|
||||
|
||||
/**
|
||||
* The current player status as int.
|
||||
*/
|
||||
public static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus";
|
||||
private static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus";
|
||||
|
||||
/**
|
||||
* Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing.
|
||||
|
@ -87,10 +92,6 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
|
|||
}
|
||||
}
|
||||
|
||||
public static long getLastPlayedFeedId() {
|
||||
return prefs.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1);
|
||||
}
|
||||
|
||||
public static long getCurrentlyPlayingMedia() {
|
||||
return prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING);
|
||||
}
|
||||
|
@ -119,4 +120,52 @@ public class PlaybackPreferences implements SharedPreferences.OnSharedPreference
|
|||
editor.putInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void writeMediaPlaying(Playable playable, PlayerStatus playerStatus, boolean stream) {
|
||||
Log.d(TAG, "Writing playback preferences");
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
if (playable == null) {
|
||||
writeNoMediaPlaying();
|
||||
} else {
|
||||
editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA, playable.getPlayableType());
|
||||
editor.putBoolean(PREF_CURRENT_EPISODE_IS_STREAM, stream);
|
||||
editor.putBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, playable.getMediaType() == MediaType.VIDEO);
|
||||
if (playable instanceof FeedMedia) {
|
||||
FeedMedia fMedia = (FeedMedia) playable;
|
||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, fMedia.getItem().getFeed().getId());
|
||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, fMedia.getId());
|
||||
} else {
|
||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, NO_MEDIA_PLAYING);
|
||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
|
||||
}
|
||||
playable.writeToPreferences(editor);
|
||||
}
|
||||
editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus));
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void writePlayerStatus(PlayerStatus playerStatus) {
|
||||
Log.d(TAG, "Writing player status playback preferences");
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus));
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private static int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) {
|
||||
int playerStatusAsInt;
|
||||
switch (playerStatus) {
|
||||
case PLAYING:
|
||||
playerStatusAsInt = PLAYER_STATUS_PLAYING;
|
||||
break;
|
||||
case PAUSED:
|
||||
playerStatusAsInt = PLAYER_STATUS_PAUSED;
|
||||
break;
|
||||
default:
|
||||
playerStatusAsInt = PLAYER_STATUS_OTHER;
|
||||
}
|
||||
return playerStatusAsInt;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -622,7 +622,7 @@ public class UserPreferences {
|
|||
.apply();
|
||||
// when updating with an interval, we assume the user wants
|
||||
// to update *now* and then every 'hours' interval thereafter.
|
||||
restartUpdateAlarm();
|
||||
AutoUpdateManager.restartUpdateAlarm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,7 +632,7 @@ public class UserPreferences {
|
|||
prefs.edit()
|
||||
.putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
|
||||
.apply();
|
||||
restartUpdateAlarm();
|
||||
AutoUpdateManager.restartUpdateAlarm();
|
||||
}
|
||||
|
||||
public static void disableAutoUpdate() {
|
||||
|
@ -882,19 +882,6 @@ public class UserPreferences {
|
|||
return getUpdateTimeOfDay().length == 2;
|
||||
}
|
||||
|
||||
public static void restartUpdateAlarm() {
|
||||
if (isAutoUpdateDisabled()) {
|
||||
AutoUpdateManager.disableAutoUpdate();
|
||||
} else if (isAutoUpdateTimeOfDay()) {
|
||||
int[] timeOfDay = getUpdateTimeOfDay();
|
||||
Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
|
||||
AutoUpdateManager.restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
|
||||
} else {
|
||||
long milliseconds = getUpdateInterval();
|
||||
AutoUpdateManager.restartUpdateIntervalAlarm(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
|
||||
*/
|
||||
|
|
|
@ -6,8 +6,7 @@ import android.content.Intent;
|
|||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.FeedUpdateUtils;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
|
||||
/**
|
||||
* Refreshes all feeds when it receives an intent
|
||||
|
@ -20,7 +19,8 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
|
|||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "Received intent");
|
||||
ClientConfig.initialize(context);
|
||||
FeedUpdateUtils.startAutoUpdate(context, null);
|
||||
|
||||
AutoUpdateManager.runOnce();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,17 +2,23 @@ package de.danoeh.antennapod.core.service;
|
|||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.FeedUpdateUtils;
|
||||
import org.awaitility.Awaitility;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
|
||||
|
||||
public class FeedUpdateWorker extends Worker {
|
||||
|
||||
private static final String TAG = "FeedUpdateWorker";
|
||||
|
||||
public static final String PARAM_RUN_ONCE = "runOnce";
|
||||
|
||||
public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
|
||||
super(context, params);
|
||||
}
|
||||
|
@ -20,16 +26,20 @@ public class FeedUpdateWorker extends Worker {
|
|||
@Override
|
||||
@NonNull
|
||||
public Result doWork() {
|
||||
final boolean isRunOnce = getInputData().getBoolean(PARAM_RUN_ONCE, false);
|
||||
Log.d(TAG, "doWork() : isRunOnce = " + isRunOnce);
|
||||
ClientConfig.initialize(getApplicationContext());
|
||||
|
||||
AtomicBoolean finished = new AtomicBoolean(false);
|
||||
FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> finished.set(true));
|
||||
Awaitility.await().until(finished::get);
|
||||
if (NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed()) {
|
||||
DBTasks.refreshAllFeeds(getApplicationContext());
|
||||
} else {
|
||||
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
|
||||
}
|
||||
|
||||
if (UserPreferences.isAutoUpdateTimeOfDay()) {
|
||||
if (!isRunOnce && UserPreferences.isAutoUpdateTimeOfDay()) {
|
||||
// WorkManager does not allow to set specific time for repeated tasks.
|
||||
// We repeatedly schedule a OneTimeWorkRequest instead.
|
||||
UserPreferences.restartUpdateAlarm();
|
||||
AutoUpdateManager.restartUpdateAlarm();
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
|
|
|
@ -13,6 +13,8 @@ import android.os.Build;
|
|||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -190,10 +192,8 @@ public class DownloadService extends Service {
|
|||
handleFailedDownload(status, downloader.getDownloadRequest());
|
||||
|
||||
if (type == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
long id = status.getFeedfileId();
|
||||
FeedMedia media = DBReader.getFeedMedia(id);
|
||||
FeedItem item;
|
||||
if (media == null || (item = media.getItem()) == null) {
|
||||
FeedItem item = getFeedItemFromId(status.getFeedfileId());
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
boolean httpNotFound = status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR
|
||||
|
@ -213,9 +213,8 @@ public class DownloadService extends Service {
|
|||
// if FeedMedia download has been canceled, fake FeedItem update
|
||||
// so that lists reload that it
|
||||
if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
|
||||
FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
|
||||
FeedItem item;
|
||||
if (media == null || (item = media.getItem()) == null) {
|
||||
FeedItem item = getFeedItemFromId(status.getFeedfileId());
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
EventBus.getDefault().post(FeedItemEvent.updated(item));
|
||||
|
@ -386,6 +385,12 @@ public class DownloadService extends Service {
|
|||
Downloader d = getDownloader(url);
|
||||
if (d != null) {
|
||||
d.cancel();
|
||||
DownloadRequester.getInstance().removeDownload(d.getDownloadRequest());
|
||||
|
||||
FeedItem item = getFeedItemFromId(d.getDownloadRequest().getFeedfileId());
|
||||
if (item != null) {
|
||||
EventBus.getDefault().post(FeedItemEvent.updated(item));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Could not cancel download with url " + url);
|
||||
}
|
||||
|
@ -430,12 +435,40 @@ public class DownloadService extends Service {
|
|||
queryDownloads();
|
||||
}
|
||||
|
||||
private Downloader getDownloader(DownloadRequest request) {
|
||||
if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
|
||||
Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
|
||||
return null;
|
||||
@VisibleForTesting
|
||||
public interface DownloaderFactory {
|
||||
@Nullable
|
||||
Downloader create(@NonNull DownloadRequest request);
|
||||
}
|
||||
|
||||
private static class DefaultDownloaderFactory implements DownloaderFactory {
|
||||
@Nullable
|
||||
@Override
|
||||
public Downloader create(@NonNull DownloadRequest request) {
|
||||
if (!URLUtil.isHttpUrl(request.getSource()) && !URLUtil.isHttpsUrl(request.getSource())) {
|
||||
Log.e(TAG, "Could not find appropriate downloader for " + request.getSource());
|
||||
return null;
|
||||
}
|
||||
return new HttpDownloader(request);
|
||||
}
|
||||
return new HttpDownloader(request);
|
||||
}
|
||||
|
||||
private static DownloaderFactory downloaderFactory = new DefaultDownloaderFactory();
|
||||
|
||||
@VisibleForTesting
|
||||
public static DownloaderFactory getDownloaderFactory() {
|
||||
return downloaderFactory;
|
||||
}
|
||||
|
||||
// public scope rather than package private,
|
||||
// because androidTest put classes in the non-standard de.test.antennapod hierarchy
|
||||
@VisibleForTesting
|
||||
public static void setDownloaderFactory(DownloaderFactory downloaderFactory) {
|
||||
DownloadService.downloaderFactory = downloaderFactory;
|
||||
}
|
||||
|
||||
private Downloader getDownloader(@NonNull DownloadRequest request) {
|
||||
return downloaderFactory.create(request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -578,6 +611,16 @@ public class DownloadService extends Service {
|
|||
syncExecutor.execute(new FailedDownloadHandler(status, request));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FeedItem getFeedItemFromId(long id) {
|
||||
FeedMedia media = DBReader.getFeedMedia(id);
|
||||
if (media != null) {
|
||||
return media.getItem();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a single Feed, parses the corresponding file and refreshes
|
||||
* information in the manager
|
||||
|
@ -654,6 +697,7 @@ public class DownloadService extends Service {
|
|||
Log.e(TAG, "FeedSyncThread was interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -688,6 +732,7 @@ public class DownloadService extends Service {
|
|||
Log.e(TAG, "FeedSyncThread was interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
Log.e(TAG, "ExecutionException in FeedSyncThread: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -983,14 +1028,17 @@ public class DownloadService extends Service {
|
|||
final FeedItem item = media.getItem();
|
||||
|
||||
try {
|
||||
DBWriter.setFeedMedia(media).get();
|
||||
|
||||
// we've received the media, we don't want to autodownload it again
|
||||
if (item != null) {
|
||||
item.setAutoDownload(false);
|
||||
// setFeedItem() signals (via EventBus) that the item has been updated,
|
||||
// so we do it after the enclosing media has been updated above,
|
||||
// to ensure subscribers will get the updated FeedMedia as well
|
||||
DBWriter.setFeedItem(item).get();
|
||||
}
|
||||
|
||||
DBWriter.setFeedMedia(media).get();
|
||||
|
||||
if (item != null && UserPreferences.enqueueDownloadedEpisodes() &&
|
||||
!DBTasks.isInQueue(DownloadService.this, item.getId())) {
|
||||
DBWriter.addQueueItem(DownloadService.this, item).get();
|
||||
|
@ -1058,7 +1106,13 @@ public class DownloadService extends Service {
|
|||
private final Runnable postDownloaderTask = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Downloader> list = Collections.unmodifiableList(downloads);
|
||||
List<Downloader> runningDownloads = new ArrayList<>();
|
||||
for (Downloader downloader : downloads) {
|
||||
if (!downloader.cancelled) {
|
||||
runningDownloads.add(downloader);
|
||||
}
|
||||
}
|
||||
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
|
||||
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
|
||||
postHandler.postDelayed(postDownloaderTask, 1500);
|
||||
}
|
||||
|
@ -1076,6 +1130,9 @@ public class DownloadService extends Service {
|
|||
private static String compileNotificationString(List<Downloader> downloads) {
|
||||
List<String> lines = new ArrayList<>(downloads.size());
|
||||
for (Downloader downloader : downloads) {
|
||||
if (downloader.cancelled) {
|
||||
continue;
|
||||
}
|
||||
StringBuilder line = new StringBuilder("• ");
|
||||
DownloadRequest request = downloader.getDownloadRequest();
|
||||
switch (request.getFeedfileType()) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.service.playback;
|
|||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
@ -23,38 +23,42 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import org.antennapod.audio.MediaPlayer;
|
||||
import de.danoeh.antennapod.core.util.playback.IPlayer;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ExoPlayerWrapper implements IPlayer {
|
||||
private static final String TAG = "ExoPlayerWrapper";
|
||||
private final Context mContext;
|
||||
private final Disposable bufferingUpdateDisposable;
|
||||
private SimpleExoPlayer mExoPlayer;
|
||||
private MediaSource mediaSource;
|
||||
private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
|
||||
private MediaPlayer.OnCompletionListener audioCompletionListener;
|
||||
private MediaPlayer.OnErrorListener audioErrorListener;
|
||||
private MediaPlayer.OnBufferingUpdateListener bufferingUpdateListener;
|
||||
private boolean shouldCheckBufferingUpdates = true;
|
||||
|
||||
|
||||
ExoPlayerWrapper(Context context) {
|
||||
mContext = context;
|
||||
mExoPlayer = createPlayer();
|
||||
|
||||
Handler handler = new Handler(); // Main thread
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (bufferingUpdateListener != null) {
|
||||
bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage());
|
||||
}
|
||||
if (shouldCheckBufferingUpdates) {
|
||||
handler.postDelayed(this, 2000);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
bufferingUpdateDisposable = Observable.interval(2, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(aLong -> {
|
||||
if (bufferingUpdateListener != null) {
|
||||
bufferingUpdateListener.onBufferingUpdate(null, mExoPlayer.getBufferedPercentage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SimpleExoPlayer createPlayer() {
|
||||
|
@ -62,6 +66,7 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
loadControl.setBufferDurationsMs(30000, 120000,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
|
||||
loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true);
|
||||
SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultRenderersFactory(mContext),
|
||||
new DefaultTrackSelector(), loadControl.createDefaultLoadControl());
|
||||
p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
|
||||
|
@ -168,7 +173,7 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
|
||||
@Override
|
||||
public void release() {
|
||||
shouldCheckBufferingUpdates = false;
|
||||
bufferingUpdateDisposable.dispose();
|
||||
if (mExoPlayer != null) {
|
||||
mExoPlayer.release();
|
||||
}
|
||||
|
@ -202,8 +207,13 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
|
||||
@Override
|
||||
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
|
||||
DataSource.Factory dataSourceFactory =
|
||||
new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, mContext.getPackageName()), null);
|
||||
Log.d(TAG, "setDataSource: " + s);
|
||||
DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(
|
||||
Util.getUserAgent(mContext, mContext.getPackageName()), null,
|
||||
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
|
||||
true);
|
||||
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext, null, httpDataSourceFactory);
|
||||
ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory);
|
||||
mediaSource = f.createMediaSource(Uri.parse(s));
|
||||
}
|
||||
|
|
|
@ -584,7 +584,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
}
|
||||
|
||||
playerLock.unlock();
|
||||
Log.d(TAG, "getPosition() -> " + retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import android.content.Intent;
|
|||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
|
@ -45,10 +44,12 @@ import com.bumptech.glide.request.target.Target;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.event.MessageEvent;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.event.ServiceEvent;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
|
@ -66,7 +67,6 @@ 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.FeedSearcher;
|
||||
import de.danoeh.antennapod.core.util.IntList;
|
||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
|
@ -74,6 +74,9 @@ import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
|||
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 io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
|
@ -212,6 +215,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
private PlaybackServiceTaskManager taskManager;
|
||||
private PlaybackServiceFlavorHelper flavorHelper;
|
||||
private PlaybackServiceStateManager stateManager;
|
||||
private Disposable positionEventTimer;
|
||||
|
||||
/**
|
||||
* Used for Lollipop notifications, Android Wear, and Android Auto.
|
||||
|
@ -331,8 +335,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
isRunning = false;
|
||||
currentMediaType = MediaType.UNKNOWN;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.unregisterOnSharedPreferenceChangeListener(prefListener);
|
||||
cancelPositionObserver();
|
||||
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(prefListener);
|
||||
if (mediaSession != null) {
|
||||
mediaSession.release();
|
||||
}
|
||||
|
@ -451,7 +455,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
notificationBuilder.loadIcon(getPlayable());
|
||||
}
|
||||
}
|
||||
startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
||||
stateManager.startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
|
@ -566,7 +570,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
|
||||
mediaPlayer.pause(!UserPreferences.isPersistNotify(), false);
|
||||
} else if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
|
||||
mediaPlayer.resume();
|
||||
} else if (status == PlayerStatus.PREPARING) {
|
||||
|
@ -590,7 +594,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
return true;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
|
||||
mediaPlayer.pause(!UserPreferences.isPersistNotify(), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -717,7 +721,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
updateMediaSession(newInfo.playerStatus);
|
||||
switch (newInfo.playerStatus) {
|
||||
case INITIALIZED:
|
||||
writePlaybackPreferences();
|
||||
PlaybackPreferences.writeMediaPlaying(mediaPlayer.getPSMPInfo().playable,
|
||||
mediaPlayer.getPSMPInfo().playerStatus, mediaPlayer.isStreaming());
|
||||
break;
|
||||
|
||||
case PREPARED:
|
||||
|
@ -734,7 +739,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
// remove notification on pause
|
||||
stateManager.stopForeground(true);
|
||||
}
|
||||
writePlayerStatusPlaybackPreferences();
|
||||
cancelPositionObserver();
|
||||
PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus());
|
||||
break;
|
||||
|
||||
case STOPPED:
|
||||
|
@ -743,8 +749,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
break;
|
||||
|
||||
case PLAYING:
|
||||
writePlayerStatusPlaybackPreferences();
|
||||
PlaybackPreferences.writePlayerStatus(mediaPlayer.getPlayerStatus());
|
||||
setupNotification(newInfo);
|
||||
setupPositionUpdater();
|
||||
stateManager.validStartCommandWasReceived();
|
||||
// set sleep timer if auto-enabled
|
||||
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
|
||||
|
@ -1023,82 +1030,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label)));
|
||||
}
|
||||
|
||||
private int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) {
|
||||
int playerStatusAsInt;
|
||||
switch (playerStatus) {
|
||||
case PLAYING:
|
||||
playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PLAYING;
|
||||
break;
|
||||
case PAUSED:
|
||||
playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PAUSED;
|
||||
break;
|
||||
default:
|
||||
playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_OTHER;
|
||||
}
|
||||
return playerStatusAsInt;
|
||||
}
|
||||
|
||||
private void writePlaybackPreferences() {
|
||||
Log.d(TAG, "Writing playback preferences");
|
||||
|
||||
SharedPreferences.Editor editor = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||
PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
||||
MediaType mediaType = mediaPlayer.getCurrentMediaType();
|
||||
boolean stream = mediaPlayer.isStreaming();
|
||||
int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus);
|
||||
|
||||
if (info.playable != null) {
|
||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
||||
info.playable.getPlayableType());
|
||||
editor.putBoolean(
|
||||
PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
|
||||
stream);
|
||||
editor.putBoolean(
|
||||
PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
|
||||
mediaType == MediaType.VIDEO);
|
||||
if (info.playable instanceof FeedMedia) {
|
||||
FeedMedia fMedia = (FeedMedia) info.playable;
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
||||
fMedia.getItem().getFeed().getId());
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
||||
fMedia.getId());
|
||||
} else {
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
}
|
||||
info.playable.writeToPreferences(editor);
|
||||
} else {
|
||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
editor.putLong(
|
||||
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
|
||||
PlaybackPreferences.NO_MEDIA_PLAYING);
|
||||
}
|
||||
editor.putInt(
|
||||
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void writePlayerStatusPlaybackPreferences() {
|
||||
Log.d(TAG, "Writing player status playback preferences");
|
||||
|
||||
SharedPreferences.Editor editor = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||
int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
|
||||
editor.putInt(PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void sendNotificationBroadcast(int type, int code) {
|
||||
Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
|
||||
intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
|
||||
|
@ -1653,6 +1584,24 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
return mediaPlayer.getVideoSize();
|
||||
}
|
||||
|
||||
private void setupPositionUpdater() {
|
||||
if (positionEventTimer != null) {
|
||||
positionEventTimer.dispose();
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting up position observer");
|
||||
positionEventTimer = Observable.interval(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(aLong ->
|
||||
EventBus.getDefault().post(new PlaybackPositionEvent(getCurrentPosition(), getDuration())));
|
||||
}
|
||||
|
||||
private void cancelPositionObserver() {
|
||||
if (positionEventTimer != null) {
|
||||
positionEventTimer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
|
||||
|
||||
private static final String TAG = "MediaSessionCompat";
|
||||
|
|
|
@ -141,7 +141,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
|
|||
stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
|
||||
PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
|
||||
numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
addAction(R.drawable.ic_media_cast_disconnect,
|
||||
addAction(R.drawable.ic_notification_cast_off,
|
||||
context.getString(R.string.cast_disconnect_label),
|
||||
stopCastingPendingIntent);
|
||||
numActions++;
|
||||
|
@ -150,7 +150,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
|
|||
// always let them rewind
|
||||
PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
|
||||
KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
|
||||
addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
|
||||
addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label), rewindButtonPendingIntent);
|
||||
if (UserPreferences.showRewindOnCompactNotification()) {
|
||||
compactActionList.add(numActions);
|
||||
}
|
||||
|
@ -159,14 +159,14 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
|
|||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
|
||||
KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
|
||||
addAction(android.R.drawable.ic_media_pause, //pause action
|
||||
addAction(R.drawable.ic_notification_pause, //pause action
|
||||
context.getString(R.string.pause_label),
|
||||
pauseButtonPendingIntent);
|
||||
compactActionList.add(numActions++);
|
||||
} else {
|
||||
PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
|
||||
KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
|
||||
addAction(android.R.drawable.ic_media_play, //play action
|
||||
addAction(R.drawable.ic_notification_play, //play action
|
||||
context.getString(R.string.play_label),
|
||||
playButtonPendingIntent);
|
||||
compactActionList.add(numActions++);
|
||||
|
@ -175,7 +175,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
|
|||
// ff follows play, then we have skip (if it's present)
|
||||
PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
|
||||
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
|
||||
addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
|
||||
addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label), ffButtonPendingIntent);
|
||||
if (UserPreferences.showFastForwardOnCompactNotification()) {
|
||||
compactActionList.add(numActions);
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ public class PlaybackServiceNotificationBuilder extends NotificationCompat.Build
|
|||
if (UserPreferences.isFollowQueue()) {
|
||||
PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
|
||||
KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
|
||||
addAction(android.R.drawable.ic_media_next,
|
||||
addAction(R.drawable.ic_notification_skip,
|
||||
context.getString(R.string.skip_episode_label),
|
||||
skipButtonPendingIntent);
|
||||
if (UserPreferences.showSkipOnCompactNotification()) {
|
||||
|
|
|
@ -7,6 +7,9 @@ import android.os.Vibrator;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -14,13 +17,12 @@ import java.util.concurrent.ScheduledFuture;
|
|||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.event.FeedItemEvent;
|
||||
import de.danoeh.antennapod.core.event.QueueEvent;
|
||||
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.util.playback.Playable;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
@ -102,6 +104,34 @@ public class PlaybackServiceTaskManager {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEvent(FeedItemEvent event) {
|
||||
// Use case: when an item in the queue has been downloaded,
|
||||
// listening to the event to ensure the downloaded item will be used.
|
||||
Log.d(TAG, "onEvent(FeedItemEvent " + event + ")");
|
||||
|
||||
for (FeedItem item : event.items) {
|
||||
if (isItemInQueue(item.getId())) {
|
||||
Log.d(TAG, "onEvent(FeedItemEvent) - some item (" + item.getId() + ") in the queue has been updated (usually downloaded). Refresh the queue.");
|
||||
cancelQueueLoader();
|
||||
loadQueue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isItemInQueue(long itemId) {
|
||||
List<FeedItem> queue = getQueueIfLoaded();
|
||||
if (queue != null) {
|
||||
for (FeedItem item : queue) {
|
||||
if (item.getId() == itemId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the queue if it is already loaded or null if it hasn't been loaded yet.
|
||||
* In order to wait until the queue has been loaded, use getQueue()
|
||||
|
|
|
@ -3,7 +3,8 @@ package de.danoeh.antennapod.core.storage;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -144,53 +145,36 @@ public final class DBTasks {
|
|||
private static final AtomicBoolean isRefreshing = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
|
||||
* Refreshes all feeds.
|
||||
* It must not be from the main thread.
|
||||
* This method might ignore subsequent calls if it is still
|
||||
* enqueuing Feeds for download from a previous call
|
||||
*
|
||||
* @param context Might be used for accessing the database
|
||||
* @param feeds List of Feeds that should be refreshed.
|
||||
*/
|
||||
public static void refreshAllFeeds(final Context context, final List<Feed> feeds) {
|
||||
refreshAllFeeds(context, feeds, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
|
||||
* enqueuing Feeds for download from a previous call
|
||||
*
|
||||
* @param context Might be used for accessing the database
|
||||
* @param feeds List of Feeds that should be refreshed.
|
||||
* @param callback Called after everything was added enqueued for download. Might be null.
|
||||
*/
|
||||
public static void refreshAllFeeds(final Context context, final List<Feed> feeds, @Nullable Runnable callback) {
|
||||
public static void refreshAllFeeds(final Context context) {
|
||||
if (!isRefreshing.compareAndSet(false, true)) {
|
||||
Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
if (feeds != null) {
|
||||
refreshFeeds(context, feeds);
|
||||
} else {
|
||||
refreshFeeds(context, DBReader.getFeedList());
|
||||
}
|
||||
isRefreshing.set(false);
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
|
||||
}
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
|
||||
refreshFeeds(context, DBReader.getFeedList());
|
||||
isRefreshing.set(false);
|
||||
|
||||
if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
|
||||
GpodnetSyncService.sendSyncIntent(context);
|
||||
}
|
||||
// Note: automatic download of episodes will be done but not here.
|
||||
// Instead it is done after all feeds have been refreshed (asynchronously),
|
||||
// in DownloadService.onDestroy()
|
||||
// See Issue #2577 for the details of the rationale
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
|
||||
prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
|
||||
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}).start();
|
||||
if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
|
||||
GpodnetSyncService.sendSyncIntent(context);
|
||||
}
|
||||
// Note: automatic download of episodes will be done but not here.
|
||||
// Instead it is done after all feeds have been refreshed (asynchronously),
|
||||
// in DownloadService.onDestroy()
|
||||
// See Issue #2577 for the details of the rationale
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -457,10 +441,9 @@ public final class DBTasks {
|
|||
/**
|
||||
* Get a FeedItem by its identifying value.
|
||||
*/
|
||||
private static FeedItem searchFeedItemByIdentifyingValue(Feed feed,
|
||||
String identifier) {
|
||||
private static FeedItem searchFeedItemByIdentifyingValue(Feed feed, String identifier) {
|
||||
for (FeedItem item : feed.getItems()) {
|
||||
if (item.getIdentifyingValue().equals(identifier)) {
|
||||
if (TextUtils.equals(item.getIdentifyingValue(), identifier)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.event.ProgressEvent;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
|
@ -38,7 +36,6 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
|
|||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.util.LongIntMap;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
// TODO Remove media column from feeditem table
|
||||
|
||||
|
@ -1478,11 +1475,9 @@ public class PodDBAdapter {
|
|||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
|
||||
final int newVersion) {
|
||||
EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database)));
|
||||
Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
|
||||
+ newVersion + ".");
|
||||
DBUpgrader.upgrade(db, oldVersion, newVersion);
|
||||
EventBus.getDefault().post(ProgressEvent.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.awaitility.core.ConditionTimeoutException;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
|
||||
import static org.awaitility.Awaitility.with;
|
||||
|
||||
public class FeedUpdateUtils {
|
||||
private static final String TAG = "FeedUpdateUtils";
|
||||
|
||||
private FeedUpdateUtils() {}
|
||||
|
||||
public static void startAutoUpdate(Context context, Runnable callback) {
|
||||
// the network check is blocking for possibly a long time: so run the logic
|
||||
// in a separate thread to prevent the code blocking the callers
|
||||
final Runnable runnable = () -> {
|
||||
try {
|
||||
with().pollInterval(1, TimeUnit.SECONDS)
|
||||
.await()
|
||||
.atMost(10, TimeUnit.SECONDS)
|
||||
.until(() -> NetworkUtils.networkAvailable() && NetworkUtils.isFeedRefreshAllowed());
|
||||
DBTasks.refreshAllFeeds(context, null, callback);
|
||||
} catch (ConditionTimeoutException ignore) {
|
||||
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
|
||||
}
|
||||
};
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,20 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IntentUtils {
|
||||
private static final String TAG = "IntentUtils";
|
||||
|
||||
private IntentUtils(){}
|
||||
|
||||
/*
|
||||
|
@ -28,4 +35,13 @@ public class IntentUtils {
|
|||
context.sendBroadcast(new Intent(action).setPackage(context.getPackageName()));
|
||||
}
|
||||
|
||||
public static void openInBrowser(Context context, String url) {
|
||||
try {
|
||||
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
context.startActivity(myIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(context, R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ public final class Optional<T> {
|
|||
* @param <T> Type of the non-existent value
|
||||
* @return an empty {@code Optional}
|
||||
*/
|
||||
public static<T> Optional<T> empty() {
|
||||
public static <T> Optional<T> empty() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<T> t = (Optional<T>) EMPTY;
|
||||
return t;
|
||||
|
|
|
@ -164,7 +164,7 @@ public class QueueSorter {
|
|||
Collections.sort(feedItems, itemComparator);
|
||||
if (spread == 0) {
|
||||
spread = feedItems.size();
|
||||
} else if (feedItems.size() % spread != 0){
|
||||
} else if (spread % feedItems.size() != 0){
|
||||
spread *= feedItems.size();
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +180,9 @@ public class QueueSorter {
|
|||
Map<Long, List<FeedItem>> spreadItems = new HashMap<>();
|
||||
for (List<FeedItem> feedItems : feeds) {
|
||||
long thisSpread = spread / feedItems.size();
|
||||
if (thisSpread == 0) {
|
||||
thisSpread = 1;
|
||||
}
|
||||
// Starting from 0 ensures we front-load, so the queue starts with one episode from
|
||||
// each feed in the queue
|
||||
long itemSpread = 0;
|
||||
|
|
|
@ -1,31 +1,55 @@
|
|||
package de.danoeh.antennapod.core.util.download;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.Data;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import androidx.work.NetworkType;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.FeedUpdateWorker;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.FeedUpdateWorker;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
|
||||
public class AutoUpdateManager {
|
||||
private static final String WORK_ID_FEED_UPDATE = FeedUpdateWorker.class.getName();
|
||||
private static final String WORK_ID_FEED_UPDATE = "de.danoeh.antennapod.core.service.FeedUpdateWorker";
|
||||
private static final String WORK_ID_FEED_UPDATE_ONCE = WORK_ID_FEED_UPDATE + "Once";
|
||||
private static final String TAG = "AutoUpdateManager";
|
||||
|
||||
private AutoUpdateManager() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Start / restart periodic auto feed refresh
|
||||
*/
|
||||
public static void restartUpdateAlarm() {
|
||||
if (UserPreferences.isAutoUpdateDisabled()) {
|
||||
disableAutoUpdate();
|
||||
} else if (UserPreferences.isAutoUpdateTimeOfDay()) {
|
||||
int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
|
||||
Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
|
||||
restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
|
||||
} else {
|
||||
long milliseconds = UserPreferences.getUpdateInterval();
|
||||
restartUpdateIntervalAlarm(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interval in which the feeds are refreshed automatically
|
||||
*/
|
||||
public static void restartUpdateIntervalAlarm(long intervalMillis) {
|
||||
private static void restartUpdateIntervalAlarm(long intervalMillis) {
|
||||
Log.d(TAG, "Restarting update alarm.");
|
||||
|
||||
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class,
|
||||
|
@ -40,7 +64,7 @@ public class AutoUpdateManager {
|
|||
/**
|
||||
* Sets time of day the feeds are refreshed automatically
|
||||
*/
|
||||
public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
|
||||
private static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
|
||||
Log.d(TAG, "Restarting update alarm.");
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
|
@ -60,6 +84,41 @@ public class AutoUpdateManager {
|
|||
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE, ExistingWorkPolicy.REPLACE, workRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run auto feed refresh once in background, as soon as what OS scheduling allows.
|
||||
*
|
||||
* Callers from UI should use {@link #runImmediate(Context)}, as it will guarantee
|
||||
* the refresh be run immediately.
|
||||
*/
|
||||
public static void runOnce() {
|
||||
Log.d(TAG, "Run auto update once, as soon as OS allows.");
|
||||
|
||||
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
|
||||
.setConstraints(getConstraints())
|
||||
.setInitialDelay(0L, TimeUnit.MILLISECONDS)
|
||||
.setInputData(new Data.Builder()
|
||||
.putBoolean(FeedUpdateWorker.PARAM_RUN_ONCE, true)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE_ONCE, ExistingWorkPolicy.REPLACE, workRequest);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Run auto feed refresh once in background immediately, using its own thread.
|
||||
*
|
||||
* Callers where the additional threads is not suitable should use {@link #runOnce()}
|
||||
*/
|
||||
public static void runImmediate(@NonNull Context context) {
|
||||
Log.d(TAG, "Run auto update immediately in background.");
|
||||
new Thread(() -> {
|
||||
DBTasks.refreshAllFeeds(context.getApplicationContext());
|
||||
}, "ManualRefreshAllFeeds").start();
|
||||
}
|
||||
|
||||
public static void disableAutoUpdate() {
|
||||
WorkManager.getInstance().cancelUniqueWork(WORK_ID_FEED_UPDATE);
|
||||
}
|
||||
|
@ -74,4 +133,5 @@ public class AutoUpdateManager {
|
|||
}
|
||||
return constraints.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package de.danoeh.antennapod.core.util.exception;
|
||||
|
||||
import android.util.Log;
|
||||
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
|
||||
import io.reactivex.exceptions.UndeliverableException;
|
||||
import io.reactivex.plugins.RxJavaPlugins;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
public class RxJavaErrorHandlerSetup {
|
||||
|
||||
private RxJavaErrorHandlerSetup() {
|
||||
|
@ -14,21 +11,14 @@ public class RxJavaErrorHandlerSetup {
|
|||
}
|
||||
|
||||
public static void setupRxJavaErrorHandler() {
|
||||
RxJavaPlugins.setErrorHandler(originalCause -> {
|
||||
Throwable e = originalCause;
|
||||
RxJavaPlugins.setErrorHandler(e -> {
|
||||
if (e instanceof UndeliverableException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e instanceof GpodnetServiceException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
|
||||
// fine, some blocking code was interrupted by a dispose call
|
||||
Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(originalCause));
|
||||
// Probably just disposed because the fragment was left
|
||||
Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(e));
|
||||
return;
|
||||
}
|
||||
Thread.currentThread().getUncaughtExceptionHandler()
|
||||
.uncaughtException(Thread.currentThread(), originalCause);
|
||||
.uncaughtException(Thread.currentThread(), e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public class ChapterReader extends ID3Reader {
|
|||
String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
|
||||
currentChapter.setLink(decodedLink);
|
||||
Log.d(TAG, "Found link: " + currentChapter.getLink());
|
||||
} catch (IllegalArgumentException _iae) {
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Log.w(TAG, "Bad URL found in ID3 data");
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,10 @@ import android.widget.ImageButton;
|
|||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
||||
import de.danoeh.antennapod.core.event.ServiceEvent;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
|
@ -69,9 +68,6 @@ public class PlaybackController {
|
|||
private final ScheduledThreadPoolExecutor schedExecutor;
|
||||
private static final int SCHED_EX_POOLSIZE = 1;
|
||||
|
||||
private MediaPositionObserver positionObserver;
|
||||
private ScheduledFuture<?> positionObserverFuture;
|
||||
|
||||
private boolean mediaInfoLoaded = false;
|
||||
private boolean released = false;
|
||||
private boolean initialized = false;
|
||||
|
@ -177,7 +173,6 @@ public class PlaybackController {
|
|||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
cancelPositionObserver();
|
||||
schedExecutor.shutdownNow();
|
||||
media = null;
|
||||
released = true;
|
||||
|
@ -254,29 +249,6 @@ public class PlaybackController {
|
|||
.getIntent());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void setupPositionObserver() {
|
||||
if (positionObserverFuture == null ||
|
||||
positionObserverFuture.isCancelled() ||
|
||||
positionObserverFuture.isDone()) {
|
||||
|
||||
Log.d(TAG, "Setting up position observer");
|
||||
positionObserver = new MediaPositionObserver();
|
||||
positionObserverFuture = schedExecutor.scheduleWithFixedDelay(
|
||||
positionObserver, MediaPositionObserver.WAITING_INTERVALL,
|
||||
MediaPositionObserver.WAITING_INTERVALL,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelPositionObserver() {
|
||||
if (positionObserverFuture != null) {
|
||||
boolean result = positionObserverFuture.cancel(true);
|
||||
Log.d(TAG, "PositionObserver cancelled. Result: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
if(service instanceof PlaybackService.LocalBinder) {
|
||||
|
@ -337,7 +309,6 @@ public class PlaybackController {
|
|||
onBufferUpdate(progress);
|
||||
break;
|
||||
case PlaybackService.NOTIFICATION_TYPE_RELOAD:
|
||||
cancelPositionObserver();
|
||||
mediaInfoLoaded = false;
|
||||
queryService();
|
||||
onReloadNotification(intent.getIntExtra(
|
||||
|
@ -447,7 +418,6 @@ public class PlaybackController {
|
|||
case PAUSED:
|
||||
clearStatusMsg();
|
||||
checkMediaInfoLoaded();
|
||||
cancelPositionObserver();
|
||||
onPositionObserverUpdate();
|
||||
updatePlayButtonAppearance(playResource, playText);
|
||||
if (!PlaybackService.isCasting() &&
|
||||
|
@ -463,7 +433,6 @@ public class PlaybackController {
|
|||
onAwaitingVideoSurface();
|
||||
setScreenOn(true);
|
||||
}
|
||||
setupPositionObserver();
|
||||
updatePlayButtonAppearance(pauseResource, pauseText);
|
||||
break;
|
||||
case PREPARING:
|
||||
|
@ -581,7 +550,6 @@ public class PlaybackController {
|
|||
*/
|
||||
public void onSeekBarStartTrackingTouch(SeekBar seekBar) {
|
||||
// interrupt position Observer, restart later
|
||||
cancelPositionObserver();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -590,7 +558,6 @@ public class PlaybackController {
|
|||
public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
|
||||
if (playbackService != null && media != null) {
|
||||
playbackService.seekTo((int) (prog * media.getDuration()));
|
||||
setupPositionObserver();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -836,19 +803,4 @@ public class PlaybackController {
|
|||
}
|
||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the current position of the media file that is playing.
|
||||
*/
|
||||
public class MediaPositionObserver implements Runnable {
|
||||
|
||||
static final int WAITING_INTERVALL = 1000;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (playbackService != null && playbackService.getStatus() == PlayerStatus.PLAYING) {
|
||||
activity.runOnUiThread(PlaybackController.this::onPositionObserverUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.9 KiB |
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M1.6,1.27L0.25,2.75L1.41,3.8C1.16,4.13 1,4.55 1,5V8H3V5.23L18.2,19H14V21H20.41L22.31,22.72L23.65,21.24M6.5,3L8.7,5H21V16.14L23,17.95V5C23,3.89 22.1,3 21,3M1,10V12A9,9 0 0,1 10,21H12C12,14.92 7.08,10 1,10M1,14V16A5,5 0 0,1 6,21H8A7,7 0 0,0 1,14M1,18V21H4A3,3 0 0,0 1,18Z" />
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
|
||||
</vector>
|
|
@ -1,5 +1,6 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
|
||||
</vector>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp">
|
||||
<path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FF0000"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
|
||||
</vector>
|
|
@ -394,6 +394,8 @@
|
|||
<string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string>
|
||||
<string name="pref_autodl_wifi_filter_title">Enable Wi-Fi filter</string>
|
||||
<string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string>
|
||||
<string name="autodl_wifi_filter_permission_title">Permission required</string>
|
||||
<string name="autodl_wifi_filter_permission_message">Location permission is required for Wi-Fi filter. Tap to grant the permission.</string>
|
||||
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
|
||||
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
|
||||
<string name="pref_parallel_downloads_title">Parallel Downloads</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.danoeh.antennapod.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.core.cast.CastManager;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
|
@ -15,6 +16,8 @@ import de.danoeh.antennapod.core.util.exception.RxJavaErrorHandlerSetup;
|
|||
* Apps using the core module of AntennaPod should register implementations of all interfaces here.
|
||||
*/
|
||||
public class ClientConfig {
|
||||
private static final String TAG = "ClientConfig";
|
||||
|
||||
private ClientConfig(){}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +47,15 @@ public class ClientConfig {
|
|||
UserPreferences.init(context);
|
||||
PlaybackPreferences.init(context);
|
||||
NetworkUtils.init(context);
|
||||
CastManager.init(context);
|
||||
// Don't initialize Cast-related logic unless it is enabled, to avoid the unnecessary
|
||||
// Google Play Service usage.
|
||||
// Down side: when the user decides to enable casting, AntennaPod needs to be restarted
|
||||
// for it to take effect.
|
||||
if (UserPreferences.isCastEnabled()) {
|
||||
CastManager.init(context);
|
||||
} else {
|
||||
Log.v(TAG, "Cast is disabled. All Cast-related initialization will be skipped.");
|
||||
}
|
||||
SleepTimerPreferences.init(context);
|
||||
RxJavaErrorHandlerSetup.setupRxJavaErrorHandler();
|
||||
initialized = true;
|
||||
|
|
|
@ -163,6 +163,10 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
|
|||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
return INSTANCE != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active {@link RemoteMediaPlayer} instance. Since there are a number of media
|
||||
* control APIs that this library do not provide a wrapper for, client applications can call
|
||||
|
|
|
@ -56,11 +56,18 @@ public class PlaybackServiceFlavorHelper {
|
|||
|
||||
PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
|
||||
this.callback = callback;
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
mediaRouter = MediaRouter.getInstance(context.getApplicationContext());
|
||||
setCastConsumer(context);
|
||||
}
|
||||
|
||||
void initializeMediaPlayer(Context context) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
callback.setMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()));
|
||||
return;
|
||||
}
|
||||
castManager = CastManager.getInstance();
|
||||
castManager.addCastConsumer(castConsumer);
|
||||
boolean isCasting = castManager.isConnected();
|
||||
|
@ -77,10 +84,16 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
void removeCastConsumer() {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
castManager.removeCastConsumer(castConsumer);
|
||||
}
|
||||
|
||||
boolean castDisconnect(boolean castDisconnect) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
if (castDisconnect) {
|
||||
castManager.disconnect();
|
||||
}
|
||||
|
@ -88,6 +101,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
switch (code) {
|
||||
case RemotePSMP.CAST_ERROR:
|
||||
callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
|
||||
|
@ -218,6 +234,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
void registerWifiBroadcastReceiver() {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (wifiBroadcastReceiver != null) {
|
||||
return;
|
||||
}
|
||||
|
@ -243,6 +262,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
void unregisterWifiBroadcastReceiver() {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (wifiBroadcastReceiver != null) {
|
||||
callback.unregisterReceiver(wifiBroadcastReceiver);
|
||||
wifiBroadcastReceiver = null;
|
||||
|
@ -250,6 +272,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
boolean onSharedPreference(String key) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
|
||||
if (!UserPreferences.isCastEnabled()) {
|
||||
if (castManager.isConnecting() || castManager.isConnected()) {
|
||||
|
@ -263,6 +288,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
void sessionStateAddActionForWear(PlaybackStateCompat.Builder sessionState, String actionName, CharSequence name, int icon) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
PlaybackStateCompat.CustomAction.Builder actionBuilder =
|
||||
new PlaybackStateCompat.CustomAction.Builder(actionName, name, icon);
|
||||
Bundle actionExtras = new Bundle();
|
||||
|
@ -273,6 +301,9 @@ public class PlaybackServiceFlavorHelper {
|
|||
}
|
||||
|
||||
void mediaSessionSetExtraForWear(MediaSessionCompat mediaSession) {
|
||||
if (!CastManager.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
Bundle sessionExtras = new Bundle();
|
||||
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_PREVIOUS, true);
|
||||
sessionExtras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_NEXT, true);
|
||||
|
|
Loading…
Reference in New Issue