Merge branch 'develop' into feat/simple-adjust-volume-per-feed

This commit is contained in:
H. Lehmann 2019-10-02 15:58:58 +02:00 committed by GitHub
commit cbee8c3043
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 978 additions and 610 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -63,6 +63,8 @@ public class PodcastApp extends Application {
EventBus.builder()
.addIndex(new ApEventBusIndex())
.addIndex(new ApCoreEventBusIndex())
.logNoSubscriberMessages(false)
.sendNoSubscriberEvent(false)
.installDefaultEventBus();
}

View File

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

View File

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

View File

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

View File

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

View File

@ -38,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;
@ -141,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;
}
@ -344,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) {
@ -429,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) {
@ -514,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;
@ -525,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);
}
}
@ -634,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)));
}

View File

@ -1,6 +1,5 @@
package de.danoeh.antennapod.fragment;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -8,7 +7,9 @@ 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;
@ -16,27 +17,53 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.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();
@ -48,7 +75,31 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
updateAutoDeleteSummary();
updateVolumeReductionValue();
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() {

View File

@ -0,0 +1,5 @@
package de.danoeh.antennapod.fragment;
public enum TransitionEffect {
NONE, FLIP, FADE
}

View File

@ -24,6 +24,7 @@ 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 {
@ -46,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();

View File

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

View File

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

View File

@ -1,20 +1,15 @@
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 {
@ -38,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);
@ -68,7 +69,8 @@ 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;
}
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ 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;
@ -435,12 +436,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);
}
/**
@ -669,6 +698,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();
}
}
@ -703,6 +733,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();
}
}
@ -998,14 +1029,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();

View File

@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import android.view.SurfaceHolder;
import com.google.android.exoplayer2.C;
@ -36,6 +37,7 @@ 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;
@ -205,6 +207,7 @@ public class ExoPlayerWrapper implements IPlayer {
@Override
public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
Log.d(TAG, "setDataSource: " + s);
DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory(
Util.getUserAgent(mContext, mContext.getPackageName()), null,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,

View File

@ -592,7 +592,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
playerLock.unlock();
Log.d(TAG, "getPosition() -> " + retVal);
return retVal;
}

View File

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

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@ -440,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;
}
}