diff options
Diffstat (limited to 'app/src/main/java')
73 files changed, 1795 insertions, 1329 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index 94d281a45..4e6b8fa29 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod; import android.app.Application; -import android.os.Build; import android.os.StrictMode; import com.joanzapata.iconify.Iconify; @@ -10,7 +9,6 @@ import com.joanzapata.iconify.fonts.MaterialModule; import de.danoeh.antennapod.core.ApCoreEventBusIndex; import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.spa.SPAUtil; import org.greenrobot.eventbus.EventBus; @@ -26,46 +24,43 @@ public class PodcastApp extends Application { } } - private static PodcastApp singleton; + private static PodcastApp singleton; - public static PodcastApp getInstance() { - return singleton; - } + public static PodcastApp getInstance() { + return singleton; + } - @Override - public void onCreate() { - super.onCreate(); + @Override + public void onCreate() { + super.onCreate(); - Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter()); + Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter()); - if(BuildConfig.DEBUG) { - StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder() - .detectLeakedSqlLiteObjects() - .penaltyLog() - .penaltyDropBox(); - builder.detectActivityLeaks(); - builder.detectLeakedClosableObjects(); - if(Build.VERSION.SDK_INT >= 16) { - builder.detectLeakedRegistrationObjects(); - } - StrictMode.setVmPolicy(builder.build()); - } + if (BuildConfig.DEBUG) { + StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder() + .detectLeakedSqlLiteObjects() + .penaltyLog() + .penaltyDropBox() + .detectActivityLeaks() + .detectLeakedClosableObjects() + .detectLeakedRegistrationObjects(); + StrictMode.setVmPolicy(builder.build()); + } - singleton = this; + singleton = this; - ClientConfig.initialize(this); + ClientConfig.initialize(this); - EventDistributor.getInstance(); - Iconify.with(new FontAwesomeModule()); - Iconify.with(new MaterialModule()); + Iconify.with(new FontAwesomeModule()); + Iconify.with(new MaterialModule()); SPAUtil.sendSPAppsQueryFeedsIntent(this); - EventBus.builder() - .addIndex(new ApEventBusIndex()) - .addIndex(new ApCoreEventBusIndex()) - .logNoSubscriberMessages(false) - .sendNoSubscriberEvent(false) - .installDefaultEventBus(); + EventBus.builder() + .addIndex(new ApEventBusIndex()) + .addIndex(new ApCoreEventBusIndex()) + .logNoSubscriberMessages(false) + .sendNoSubscriberEvent(false) + .installDefaultEventBus(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java deleted file mode 100644 index ef7ea2b16..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ /dev/null @@ -1,158 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.content.res.TypedArray; -import android.graphics.Color; -import android.os.Build; -import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.webkit.WebSettings; -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; -import java.io.InputStream; -import java.nio.charset.Charset; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import io.reactivex.Single; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Displays the 'about' screen - */ -public class AboutActivity extends AppCompatActivity { - - private static final String TAG = AboutActivity.class.getSimpleName(); - - private WebView webView; - private LinearLayout webViewContainer; - private Disposable disposable; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); - super.onCreate(savedInstanceState); - getSupportActionBar().setDisplayShowHomeEnabled(true); - setContentView(R.layout.about); - webViewContainer = findViewById(R.id.webViewContainer); - webView = findViewById(R.id.webViewAbout); - webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { - webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - webView.setBackgroundColor(Color.TRANSPARENT); - webView.setWebViewClient(new WebViewClient() { - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (url.startsWith("http")) { - IntentUtils.openInBrowser(AboutActivity.this, url); - return true; - } else { - url = url.replace("file:///android_asset/", ""); - loadAsset(url); - return true; - } - } - - }); - loadAsset("about.html"); - } - - private void loadAsset(String filename) { - disposable = Single.create(subscriber -> { - InputStream input = null; - try { - TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes( - new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background, - R.attr.about_screen_card_background, R.attr.about_screen_card_border}); - String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0)); - String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0)); - String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0)); - String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0)); - res.recycle(); - input = getAssets().open(filename); - String webViewData = IOUtils.toString(input, Charset.defaultCharset()); - if (!webViewData.startsWith("<!DOCTYPE html>")) { - webViewData = webViewData.replace("%", "%"); - webViewData = - "<!DOCTYPE html>" + - "<html>" + - "<head>" + - " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" + - " <style type=\"text/css\">" + - " @font-face {" + - " font-family: 'Roboto-Light';" + - " src: url('file:///android_asset/Roboto-Light.ttf');" + - " }" + - " * {" + - " color: @fontcolor@;" + - " font-family: roboto-Light;" + - " font-size: 8pt;" + - " }" + - " </style>" + - "</head><body><p>" + webViewData + "</p></body></html>"; - webViewData = webViewData.replace("\n", "<br/>"); - } - webViewData = webViewData.replace("@fontcolor@", fontColor); - webViewData = webViewData.replace("@background@", backgroundColor); - webViewData = webViewData.replace("@card_background@", cardBackground); - webViewData = webViewData.replace("@card_border@", cardBorder); - subscriber.onSuccess(webViewData); - } catch (IOException e) { - Log.e(TAG, Log.getStackTraceString(e)); - subscriber.onError(e); - } finally { - IOUtils.closeQuietly(input); - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - webViewData -> - webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()), - error -> Log.e(TAG, Log.getStackTraceString(error)) - ); - } - - @Override - public void onBackPressed() { - if (webView.canGoBack()) { - webView.goBack(); - } else { - super.onBackPressed(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - return true; - } else { - return super.onOptionsItemSelected(item); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (disposable != null) { - disposable.dispose(); - } - if (webViewContainer != null && webView != null) { - webViewContainer.removeAllViews(); - webView.destroy(); - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java index 8eb0b1e0b..7f8c14b03 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; -import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; +import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.dialog.VariableSpeedDialog; @@ -83,7 +83,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { } float speed = 1.0f; if(controller.canSetPlaybackSpeed()) { - speed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(controller.getMedia()); + speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia()); } String speedStr = new DecimalFormat("0.00").format(speed); txtvPlaybackSpeed.setText(speedStr); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index 6a38f8f0a..0023e6d7f 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -34,6 +34,8 @@ import android.widget.Toast; import com.bumptech.glide.Glide; +import de.danoeh.antennapod.core.event.FeedListUpdateEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; import org.greenrobot.eventbus.EventBus; @@ -48,7 +50,6 @@ 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.QueueEvent; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -84,9 +85,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi private static final String TAG = "MainActivity"; - private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE - | EventDistributor.UNREAD_ITEMS_UPDATE; - public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag"; @@ -319,8 +317,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi fragment = new AddFeedFragment(); break; case SubscriptionFragment.TAG: - SubscriptionFragment subscriptionFragment = new SubscriptionFragment(); - fragment = subscriptionFragment; + fragment = new SubscriptionFragment(); break; default: // default to the queue @@ -490,7 +487,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); RatingDialog.init(this); } @@ -518,7 +514,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi @Override protected void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); if (disposable != null) { disposable.dispose(); @@ -817,16 +812,16 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi snackbar.show(); } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Subscribe + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + loadData(); + } - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EVENTS & arg) != 0) { - Log.d(TAG, "Received contentUpdate Intent."); - loadData(); - } - } - }; + + @Subscribe + public void onFeedListChanged(FeedListUpdateEvent event) { + loadData(); + } private void handleNavIntent() { Log.d(TAG, "handleNavIntent()"); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index a060e258a..538ed1231 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -28,7 +28,6 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import android.widget.Toast; -import com.afollestad.materialdialogs.MaterialDialog; import com.bumptech.glide.Glide; import com.joanzapata.iconify.IconDrawable; import com.joanzapata.iconify.fonts.FontAwesomeIcons; @@ -64,6 +63,7 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.apache.commons.lang3.StringUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -391,10 +391,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements | Intent.FLAG_ACTIVITY_NEW_TASK); View cover = findViewById(R.id.imgvCover); - if (cover != null && Build.VERSION.SDK_INT >= 16) { - ActivityOptionsCompat options = ActivityOptionsCompat. - makeSceneTransitionAnimation(MediaplayerActivity.this, - cover, "coverTransition"); + if (cover != null) { + ActivityOptionsCompat options = ActivityOptionsCompat + .makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition"); startActivity(intent, options.toBundle()); } else { startActivity(intent); @@ -426,19 +425,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements case R.id.disable_sleeptimer_item: if (controller.serviceAvailable()) { - MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this); - stDialog.title(R.string.sleep_timer_label); - stDialog.content(getString(R.string.time_left_label) - + Converter.getDurationStringLong((int) controller - .getSleepTimerTimeLeft())); - stDialog.positiveText(R.string.disable_sleeptimer_label); - stDialog.negativeText(R.string.cancel_label); - stDialog.onPositive((dialog, which) -> { - dialog.dismiss(); - controller.disableSleepTimer(); - }); - stDialog.onNegative((dialog, which) -> dialog.dismiss()); - stDialog.build().show(); + new AlertDialog.Builder(this) + .setTitle(R.string.sleep_timer_label) + .setMessage(getString(R.string.time_left_label) + + Converter.getDurationStringLong((int) controller + .getSleepTimerTimeLeft())) + .setPositiveButton(R.string.disable_sleeptimer_label, (dialog, which) + -> controller.disableSleepTimer()) + .setNegativeButton(R.string.cancel_label, null) + .show(); } break; case R.id.set_sleeptimer_item: @@ -504,7 +499,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements private static String getWebsiteLinkWithFallback(Playable media) { if (media == null) { return null; - } else if (media.getWebsiteLink() != null) { + } else if (StringUtils.isNotBlank(media.getWebsiteLink())) { return media.getWebsiteLink(); } else if (media instanceof FeedMedia) { return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem()); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java index 016168b45..21f4ad5be 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -36,8 +36,8 @@ import de.danoeh.antennapod.R; 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.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.MessageEvent; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -119,7 +119,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem if (disposable != null) { disposable.dispose(); } - EventDistributor.getInstance().unregister(contentUpdate); saveCurrentFragment(); } @@ -171,7 +170,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem @Override protected void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); loadData(); } @@ -445,16 +443,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem snackbar.show(); } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) { - Log.d(TAG, "Received contentUpdate Intent."); - loadData(); - } - } - }; + @Subscribe + public void onFeedListChanged(FeedListUpdateEvent event) { + loadData(); + } private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index ede6e6962..af11e9219 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -10,7 +10,6 @@ import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.UiThread; import androidx.core.app.NavUtils; -import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.text.TextUtils; @@ -22,22 +21,19 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.Spinner; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; +import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import org.apache.commons.lang3.StringUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import java.io.File; import java.io.IOException; @@ -49,7 +45,6 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.event.DownloadEvent; -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.FeedPreferences; @@ -95,60 +90,31 @@ public class OnlineFeedViewActivity extends AppCompatActivity { public static final String ARG_TITLE = "title"; private static final int RESULT_ERROR = 2; private static final String TAG = "OnlineFeedViewActivity"; - private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE; private volatile List<Feed> feeds; private Feed feed; private String selectedDownloadUrl; private Downloader downloader; private boolean isPaused; + private boolean didPressSubscribe = false; private Dialog dialog; - + private ListView listView; private Button subscribeButton; + private ProgressBar progressBar; private Disposable download; private Disposable parser; private Disposable updater; - private final EventDistributor.EventListener listener = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) { - updater = Observable.fromCallable(DBReader::getFeedList) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - feeds -> { - OnlineFeedViewActivity.this.feeds = feeds; - setSubscribeButtonState(feed); - }, error -> Log.e(TAG, Log.getStackTraceString(error)) - ); - } else if ((arg & EVENTS) != 0) { - setSubscribeButtonState(feed); - } - } - }; - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(DownloadEvent event) { - Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - setSubscribeButtonState(feed); - } @Override protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); + setTheme(UserPreferences.getTranslucentTheme()); super.onCreate(savedInstanceState); - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - } - - if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) { - actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE)); - } - StorageUtils.checkStorageAvailability(this); + setContentView(R.layout.onlinefeedview_activity); + listView = findViewById(R.id.listview); + progressBar = findViewById(R.id.progressBar); String feedUrl = null; if (getIntent().hasExtra(ARG_FEEDURL)) { @@ -157,9 +123,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { || TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND) ? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString(); - if (actionBar != null) { - actionBar.setTitle(R.string.add_feed_label); - } } if (feedUrl == null) { @@ -184,26 +147,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity { * Displays a progress indicator. */ private void setLoadingLayout() { - RelativeLayout rl = new RelativeLayout(this); - RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT); - - ProgressBar pb = new ProgressBar(this); - pb.setIndeterminate(true); - RelativeLayout.LayoutParams pbLayoutParams = new RelativeLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT); - pbLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); - rl.addView(pb, pbLayoutParams); - addContentView(rl, rlLayoutParams); + progressBar.setVisibility(View.VISIBLE); + findViewById(R.id.feedDisplay).setVisibility(View.GONE); } @Override protected void onStart() { super.onStart(); isPaused = false; - EventDistributor.getInstance().register(listener); EventBus.getDefault().register(this); } @@ -211,7 +162,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { protected void onStop() { super.onStop(); isPaused = true; - EventDistributor.getInstance().unregister(listener); EventBus.getDefault().unregister(this); if (downloader != null && !downloader.isFinished()) { downloader.cancel(); @@ -252,6 +202,12 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } @Override + public void finish() { + super.finish(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: @@ -301,7 +257,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) { if (!isFinishing() && !isPaused) { dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this, - R.string.authentication_notification_title, downloader.getDownloadRequest().getSource()); + R.string.authentication_notification_title, + downloader.getDownloadRequest().getSource()).create(); dialog.show(); } } else { @@ -313,6 +270,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } } + @Subscribe + public void onFeedListChanged(FeedListUpdateEvent event) { + updater = Observable.fromCallable(DBReader::getFeedList) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + feeds -> { + OnlineFeedViewActivity.this.feeds = feeds; + handleUpdatedFeedStatus(feed); + }, error -> Log.e(TAG, Log.getStackTraceString(error)) + ); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(DownloadEvent event) { + Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); + handleUpdatedFeedStatus(feed); + } + private void parseFeed() { if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) { throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called"); @@ -368,20 +344,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity { * This method is executed on a background thread */ private void beforeShowFeedInformation(Feed feed) { - final HtmlToPlainText formatter = new HtmlToPlainText(); - if(Feed.TYPE_ATOM1.equals(feed.getType()) && feed.getDescription() != null) { - // remove HTML tags from descriptions - Log.d(TAG, "Removing HTML from feed description"); - Document feedDescription = Jsoup.parse(feed.getDescription()); - feed.setDescription(StringUtils.trim(formatter.getPlainText(feedDescription))); - } + Log.d(TAG, "Removing HTML from feed description"); + + feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription())); + Log.d(TAG, "Removing HTML from shownotes"); if (feed.getItems() != null) { for (FeedItem item : feed.getItems()) { - if (item.getDescription() != null) { - Document itemDescription = Jsoup.parse(item.getDescription()); - item.setDescription(StringUtils.trim(formatter.getPlainText(itemDescription))); - } + item.setDescription(HtmlToPlainText.getPlainText(item.getDescription())); } } } @@ -391,30 +361,27 @@ public class OnlineFeedViewActivity extends AppCompatActivity { * This method is executed on the GUI thread. */ private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) { - setContentView(R.layout.listview_activity); - + progressBar.setVisibility(View.GONE); + findViewById(R.id.feedDisplay).setVisibility(View.VISIBLE); this.feed = feed; this.selectedDownloadUrl = feed.getDownload_url(); - EventDistributor.getInstance().register(listener); - ListView listView = findViewById(R.id.listview); listView.setSelector(android.R.color.transparent); - LayoutInflater inflater = LayoutInflater.from(this); - View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false); - listView.addHeaderView(header); - listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems())); - ImageView cover = header.findViewById(R.id.imgvCover); - ImageView headerBackground = header.findViewById(R.id.imgvBackground); - header.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); - header.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); + ImageView cover = findViewById(R.id.imgvCover); + ImageView headerBackground = findViewById(R.id.imgvBackground); + findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); + findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); headerBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - TextView title = header.findViewById(R.id.txtvTitle); - TextView author = header.findViewById(R.id.txtvAuthor); + TextView title = findViewById(R.id.txtvTitle); + TextView author = findViewById(R.id.txtvAuthor); + Spinner spAlternateUrls = findViewById(R.id.spinnerAlternateUrls); + + View header = View.inflate(this, R.layout.onlinefeedview_header, null); + listView.addHeaderView(header); TextView description = header.findViewById(R.id.txtvDescription); - Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls); - subscribeButton = header.findViewById(R.id.butSubscribe); + subscribeButton = findViewById(R.id.butSubscribe); if (StringUtils.isNotBlank(feed.getImageUrl())) { Glide.with(this) @@ -442,11 +409,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { description.setText(feed.getDescription()); subscribeButton.setOnClickListener(v -> { - if(feedInFeedlist(feed)) { - // feed.getId() is always 0, we have to retrieve the id from the feed list from - // the database - Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed)); - startActivity(intent); + if (feedInFeedlist(feed)) { + openFeed(); } else { Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle()); f.setPreferences(feed.getPreferences()); @@ -457,7 +421,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { Log.e(TAG, Log.getStackTraceString(e)); DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage()); } - setSubscribeButtonState(feed); + didPressSubscribe = true; + handleUpdatedFeedStatus(feed); } }); @@ -503,17 +468,28 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } }); } - setSubscribeButtonState(feed); + handleUpdatedFeedStatus(feed); + } + + private void openFeed() { + // feed.getId() is always 0, we have to retrieve the id from the feed list from + // the database + Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed)); + finish(); + startActivity(intent); } - private void setSubscribeButtonState(Feed feed) { + private void handleUpdatedFeedStatus(Feed feed) { if (subscribeButton != null && feed != null) { if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) { subscribeButton.setEnabled(false); - subscribeButton.setText(R.string.downloading_label); + subscribeButton.setText(R.string.subscribing_label); } else if (feedInFeedlist(feed)) { subscribeButton.setEnabled(true); subscribeButton.setText(R.string.open_podcast); + if (didPressSubscribe) { + openFeed(); + } } else { subscribeButton.setEnabled(true); subscribeButton.setText(R.string.subscribe_label); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java index 9caff0fc0..d7a4b9517 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java @@ -6,12 +6,11 @@ import android.net.Uri; import android.os.Build; import android.os.Environment; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; -import com.afollestad.materialdialogs.MaterialDialog; - import org.apache.commons.lang3.ArrayUtils; import java.io.InputStreamReader; @@ -30,55 +29,55 @@ import de.danoeh.antennapod.core.util.LangUtils; public class OpmlImportBaseActivity extends AppCompatActivity { private static final String TAG = "OpmlImportBaseActivity"; - private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5; + private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5; private OpmlImportWorker importWorker; - @Nullable private Uri uri; - - /** - * Handles the choices made by the user in the OpmlFeedChooserActivity and - * starts the OpmlFeedQueuer if necessary. - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d(TAG, "Received result"); - if (resultCode == RESULT_CANCELED) { - Log.d(TAG, "Activity was cancelled"); + @Nullable private Uri uri; + + /** + * Handles the choices made by the user in the OpmlFeedChooserActivity and + * starts the OpmlFeedQueuer if necessary. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d(TAG, "Received result"); + if (resultCode == RESULT_CANCELED) { + Log.d(TAG, "Activity was cancelled"); if (finishWhenCanceled()) { - finish(); - } - } else { - int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS); - if (selected != null && selected.length > 0) { - OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) { - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP - | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - - }; - queuer.executeAsync(); - } else { - Log.d(TAG, "No items were selected"); - } - } - } - - void importUri(@Nullable Uri uri) { + finish(); + } + } else { + int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS); + if (selected != null && selected.length > 0) { + OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) { + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + }; + queuer.executeAsync(); + } else { + Log.d(TAG, "No items were selected"); + } + } + } + + void importUri(@Nullable Uri uri) { if(uri == null) { - new MaterialDialog.Builder(this) - .content(R.string.opml_import_error_no_file) - .positiveText(android.R.string.ok) + new AlertDialog.Builder(this) + .setMessage(R.string.opml_import_error_no_file) + .setPositiveButton(android.R.string.ok, null) .show(); return; } - this.uri = uri; + this.uri = uri; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - uri.toString().contains(Environment.getExternalStorageDirectory().toString())) { + uri.toString().contains(Environment.getExternalStorageDirectory().toString())) { int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { requestPermission(); @@ -88,30 +87,28 @@ public class OpmlImportBaseActivity extends AppCompatActivity { startImport(); } - private void requestPermission() { - String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE }; - ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE); - } - - @Override - public void onRequestPermissionsResult(int requestCode, - String[] permissions, - int[] grantResults) { - if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) { - return; - } - if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) { - startImport(); - } else { - new MaterialDialog.Builder(this) - .content(R.string.opml_import_ask_read_permission) - .positiveText(android.R.string.ok) - .negativeText(R.string.cancel_label) - .onPositive((dialog, which) -> requestPermission()) - .onNegative((dialog, which) -> finish()) + private void requestPermission() { + String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE }; + ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String[] permissions, + int[] grantResults) { + if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) { + return; + } + if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) { + startImport(); + } else { + new AlertDialog.Builder(this) + .setMessage(R.string.opml_import_ask_read_permission) + .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission()) + .setNegativeButton(R.string.cancel_label, (dialog, which) -> finish()) .show(); - } - } + } + } /** Starts the import process. */ private void startImport() { @@ -136,10 +133,10 @@ public class OpmlImportBaseActivity extends AppCompatActivity { importWorker.executeAsync(); } catch (Exception e) { Log.d(TAG, Log.getStackTraceString(e)); - String message = getString(R.string.opml_reader_error); - new MaterialDialog.Builder(this) - .content(message + " " + e.getMessage()) - .positiveText(android.R.string.ok) + String message = getString(R.string.opml_reader_error); + new AlertDialog.Builder(this) + .setMessage(message + " " + e.getMessage()) + .setPositiveButton(android.R.string.ok, null) .show(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java index c9c9a0e2c..8527949b0 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java @@ -16,8 +16,6 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Button; -import com.afollestad.materialdialogs.MaterialDialog; - import java.io.File; import de.danoeh.antennapod.R; @@ -28,22 +26,22 @@ import de.danoeh.antennapod.dialog.ChooseDataFolderDialog; /** Is show if there is now external storage available. */ public class StorageErrorActivity extends AppCompatActivity { - private static final String TAG = "StorageErrorActivity"; + private static final String TAG = "StorageErrorActivity"; private static final String[] EXTERNAL_STORAGE_PERMISSIONS = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 42; - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(UserPreferences.getTheme()); + super.onCreate(savedInstanceState); - setContentView(R.layout.storage_error); + setContentView(R.layout.storage_error); - Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder); - btnChooseDataFolder.setOnClickListener(v -> { + Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder); + btnChooseDataFolder.setOnClickListener(v -> { if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { showChooseDataFolderDialog(); @@ -82,11 +80,10 @@ public class StorageErrorActivity extends AppCompatActivity { } if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { - new MaterialDialog.Builder(this) - .content(R.string.choose_data_directory_permission_rationale) - .positiveText(android.R.string.ok) - .onPositive((dialog, which) -> requestPermission()) - .onNegative((dialog, which) -> finish()) + new AlertDialog.Builder(this) + .setMessage(R.string.choose_data_directory_permission_rationale) + .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission()) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> finish()) .show(); } } @@ -101,15 +98,15 @@ public class StorageErrorActivity extends AppCompatActivity { } } - @Override - protected void onPause() { - super.onPause(); - try { - unregisterReceiver(mediaUpdate); - } catch (IllegalArgumentException e) { + @Override + protected void onPause() { + super.onPause(); + try { + unregisterReceiver(mediaUpdate); + } catch (IllegalArgumentException e) { Log.e(TAG, Log.getStackTraceString(e)); - } - } + } + } // see PreferenceController.showChooseDataFolderDialog() private void showChooseDataFolderDialog() { @@ -123,9 +120,9 @@ public class StorageErrorActivity extends AppCompatActivity { }); } - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == Activity.RESULT_OK && - requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK && + requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) { String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR); File path; @@ -138,46 +135,46 @@ public class StorageErrorActivity extends AppCompatActivity { return; } String message = null; - if(!path.exists()) { - message = String.format(getString(R.string.folder_does_not_exist_error), dir); - } else if(!path.canRead()) { - message = String.format(getString(R.string.folder_not_readable_error), dir); - } else if(!path.canWrite()) { - message = String.format(getString(R.string.folder_not_writable_error), dir); - } - - if(message == null) { - Log.d(TAG, "Setting data folder: " + dir); - UserPreferences.setDataFolder(dir); - leaveErrorState(); - } else { - AlertDialog.Builder ab = new AlertDialog.Builder(this); - ab.setMessage(message); - ab.setPositiveButton(android.R.string.ok, null); - ab.show(); - } - } - } - - private void leaveErrorState() { - finish(); - startActivity(new Intent(this, MainActivity.class)); - } - - private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) { - if (intent.getBooleanExtra("read-only", true)) { - Log.d(TAG, "Media was mounted; Finishing activity"); - leaveErrorState(); - } else { - Log.d(TAG, "Media seemed to have been mounted read only"); - } - } - } - - }; + if(!path.exists()) { + message = String.format(getString(R.string.folder_does_not_exist_error), dir); + } else if(!path.canRead()) { + message = String.format(getString(R.string.folder_not_readable_error), dir); + } else if(!path.canWrite()) { + message = String.format(getString(R.string.folder_not_writable_error), dir); + } + + if(message == null) { + Log.d(TAG, "Setting data folder: " + dir); + UserPreferences.setDataFolder(dir); + leaveErrorState(); + } else { + AlertDialog.Builder ab = new AlertDialog.Builder(this); + ab.setMessage(message); + ab.setPositiveButton(android.R.string.ok, null); + ab.show(); + } + } + } + + private void leaveErrorState() { + finish(); + startActivity(new Intent(this, MainActivity.class)); + } + + private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) { + if (intent.getBooleanExtra("read-only", true)) { + Log.d(TAG, "Media was mounted; Finishing activity"); + leaveErrorState(); + } else { + Log.d(TAG, "Media seemed to have been mounted read only"); + } + } + } + + }; } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index 2d28ea561..0c6ae2645 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -151,10 +151,7 @@ public class VideoplayerActivity extends MediaplayerActivity { videoview.getHolder().addCallback(surfaceHolderCallback); videoframe.setOnTouchListener(onVideoviewTouched); videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider - - if (Build.VERSION.SDK_INT >= 16) { - videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } + videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); videoOverlay.setFitsSystemWindows(true); setupVideoControlsToggler(); @@ -351,9 +348,9 @@ public class VideoplayerActivity extends MediaplayerActivity { controls.startAnimation(animation); } } - int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); videoOverlay.setFitsSystemWindows(true); videoOverlay.setVisibility(View.GONE); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java new file mode 100644 index 000000000..474b96c38 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java @@ -0,0 +1,107 @@ +package de.danoeh.antennapod.activity; + +import android.Manifest; +import android.app.WallpaperManager; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.widget.ImageView; +import android.widget.RemoteViews; +import androidx.appcompat.app.AppCompatActivity; + +import android.appwidget.AppWidgetManager; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import androidx.core.content.ContextCompat; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.receiver.PlayerWidget; +import de.danoeh.antennapod.core.service.PlayerWidgetJobService; + +public class WidgetConfigActivity extends AppCompatActivity { + private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + + private SeekBar opacitySeekBar; + private TextView opacityTextView; + private RelativeLayout widgetPreview; + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(UserPreferences.getTheme()); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_widget_config); + + Intent configIntent = getIntent(); + Bundle extras = configIntent.getExtras(); + if (extras != null) { + appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + } + + Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + setResult(RESULT_CANCELED, resultValue); + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish(); + } + + displayDeviceBackground(); + opacityTextView = findViewById(R.id.widget_opacity_textView); + opacitySeekBar = findViewById(R.id.widget_opacity_seekBar); + widgetPreview = findViewById(R.id.widgetLayout); + findViewById(R.id.butConfirm).setOnClickListener(this::confirmCreateWidget); + opacitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + + @Override + public void onProgressChanged(SeekBar seekBar, int i, boolean b) { + opacityTextView.setText(seekBar.getProgress() + "%"); + int color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress()); + widgetPreview.setBackgroundColor(color); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + + }); + } + + private void displayDeviceBackground() { + int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); + if (Build.VERSION.SDK_INT < 27 || permission == PackageManager.PERMISSION_GRANTED) { + final WallpaperManager wallpaperManager = WallpaperManager.getInstance(this); + final Drawable wallpaperDrawable = wallpaperManager.getDrawable(); + ImageView background = findViewById(R.id.widget_config_background); + background.setImageDrawable(wallpaperDrawable); + } + } + + private void confirmCreateWidget(View v) { + int backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress()); + + SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor); + editor.apply(); + + Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + setResult(RESULT_OK, resultValue); + finish(); + PlayerWidgetJobService.updateWidget(this); + } + + private int getColorWithAlpha(int color, int opacity) { + return (int) Math.round(0xFF * (0.01 * opacity)) * 0x1000000 + color; + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index 5b735cd1f..9cd5cc3ab 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -35,10 +35,12 @@ import de.danoeh.antennapod.core.feed.FeedMedia; 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.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.fragment.ItemFragment; +import de.danoeh.antennapod.fragment.ItemPagerFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; /** @@ -53,7 +55,6 @@ 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; @@ -171,7 +172,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR if (media.isCurrentlyPlaying()) { holder.container.setBackgroundColor(playingBackGroundColor); - currentlyPlayingItem = holder; } else { holder.container.setBackgroundColor(normalBackGroundColor); } @@ -194,29 +194,13 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.butSecondary.setTag(item); new CoverLoader(mainActivityRef.get()) - .withUri(item.getImageLocation()) + .withUri(ImageResourceUtils.getImageLocation(item)) .withFallbackUri(item.getFeed().getImageLocation()) .withPlaceholderView(holder.placeholder) .withCoverView(holder.cover) .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; @@ -262,7 +246,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR MainActivity mainActivity = mainActivityRef.get(); if (mainActivity != null) { long[] ids = itemAccess.getItemsIds().toArray(); - mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition())); + mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, getAdapterPosition())); } } @@ -301,6 +285,14 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item); } + public boolean isCurrentlyPlayingItem() { + return item.getMedia() != null && item.getMedia().isCurrentlyPlaying(); + } + + public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) { + progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration())); + } + } public interface ItemAccess { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java index 9014de525..e3ca5b5a5 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.adapter; import android.app.Dialog; import android.content.Context; +import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; @@ -20,7 +21,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.dialog.ChooseDataFolderDialog; -import me.zhanghai.android.materialprogressbar.MaterialProgressBar; public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.ViewHolder> { @@ -105,7 +105,7 @@ public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.Vi private TextView path; private TextView size; private RadioButton radioButton; - private MaterialProgressBar progressBar; + private ProgressBar progressBar; ViewHolder(View itemView) { super(itemView); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java index b8764c2ae..4d66fd486 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter; import android.content.Context; import android.os.Build; -import androidx.core.content.ContextCompat; import android.text.Layout; import android.text.format.DateUtils; import android.util.Log; @@ -13,6 +12,8 @@ import android.widget.BaseAdapter; import android.widget.TextView; import android.widget.Toast; +import androidx.core.content.ContextCompat; + import com.joanzapata.iconify.widget.IconButton; import com.joanzapata.iconify.widget.IconTextView; @@ -24,6 +25,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.storage.DownloadRequester; /** Displays a list of DownloadStatus entries. */ public class DownloadLogAdapter extends BaseAdapter { @@ -132,7 +134,7 @@ public class DownloadLogAdapter extends BaseAdapter { FeedMedia media = DBReader.getFeedMedia(holder.id); if (media != null) { try { - DBTasks.downloadFeedItems(context, media.getItem()); + DownloadRequester.getInstance().downloadMedia(context, media.getItem()); Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); } catch (DownloadRequestException e) { e.printStackTrace(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java index 98d55dd97..b083908a8 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -17,9 +17,9 @@ import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.glide.ApGlideSettings; -import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; /** * Shows a list of downloaded episodes @@ -79,7 +79,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { } Glide.with(context) - .load(item.getImageLocation()) + .load(ImageResourceUtils.getImageLocation(item)) .apply(new RequestOptions() .placeholder(R.color.light_gray) .error(R.color.light_gray) diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java index df7ec46e0..66fa79a4e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java @@ -59,6 +59,8 @@ public class FeedDiscoverAdapter extends BaseAdapter { final PodcastSearchResult podcast = getItem(position); + holder.imageView.setContentDescription(podcast.title); + Glide.with(mainActivityRef.get()) .load(podcast.imageUrl) .apply(new RequestOptions() diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index fcdcb4ba6..5ccec0ade 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.adapter; import android.os.Build; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.view.MotionEventCompat; @@ -26,10 +25,10 @@ import android.widget.TextView; import com.joanzapata.iconify.Iconify; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import de.danoeh.antennapod.fragment.ItemPagerFragment; 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; @@ -40,10 +39,10 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ThemeUtils; -import de.danoeh.antennapod.fragment.ItemFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; /** @@ -60,7 +59,6 @@ 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; @@ -83,11 +81,13 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap notifyDataSetChanged(); } + @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false); return new ViewHolder(view); } + @Override public void onBindViewHolder(ViewHolder holder, int pos) { FeedItem item = itemAccess.getItem(pos); holder.bind(item); @@ -97,18 +97,6 @@ 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; @@ -124,12 +112,6 @@ 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, @@ -181,7 +163,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap if (activity != null) { long[] ids = itemAccess.getQueueIds().toArray(); int position = ArrayUtils.indexOf(ids, item.getId()); - activity.loadChildFragment(ItemFragment.newInstance(ids, position)); + activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); } } @@ -309,7 +291,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap if(media.isCurrentlyPlaying()) { container.setBackgroundColor(playingBackGroundColor); - currentlyPlayingItem = this; } else { container.setBackgroundColor(normalBackGroundColor); } @@ -322,13 +303,22 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap butSecondary.setTag(item); new CoverLoader(mainActivity.get()) - .withUri(item.getImageLocation()) + .withUri(ImageResourceUtils.getImageLocation(item)) .withFallbackUri(item.getFeed().getImageLocation()) .withPlaceholderView(placeholder) .withCoverView(cover) .load(); } + public boolean isCurrentlyPlayingItem() { + return item.getMedia() != null && item.getMedia().isCurrentlyPlaying(); + } + + public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) { + progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration())); + progressLeft.setText(Converter.getDurationStringLong(event.getPosition())); + progressRight.setText(Converter.getDurationStringLong(event.getDuration())); + } } public interface ItemAccess { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java new file mode 100644 index 000000000..10bda4efa --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java @@ -0,0 +1,59 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.RequestOptions; +import de.danoeh.antennapod.R; + +import java.util.List; + +/** + * Displays a list of items that have a subtitle and an icon. + */ +public class SimpleIconListAdapter<T extends SimpleIconListAdapter.ListItem> extends ArrayAdapter<T> { + private final Context context; + private final List<T> listItems; + + public SimpleIconListAdapter(Context context, List<T> listItems) { + super(context, R.layout.simple_icon_list_item, listItems); + this.context = context; + this.listItems = listItems; + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + view = View.inflate(context, R.layout.simple_icon_list_item, null); + } + + ListItem item = listItems.get(position); + ((TextView) view.findViewById(R.id.title)).setText(item.title); + ((TextView) view.findViewById(R.id.subtitle)).setText(item.subtitle); + Glide.with(context) + .load(item.imageUrl) + .apply(new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .fitCenter() + .dontAnimate()) + .into(((ImageView) view.findViewById(R.id.icon))); + return view; + } + + public static class ListItem { + public final String title; + public final String subtitle; + public final String imageUrl; + + public ListItem(String title, String subtitle, String imageUrl) { + this.title = title; + this.subtitle = subtitle; + this.imageUrl = imageUrl; + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java index 5d52eb263..3141c6046 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java @@ -84,6 +84,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI if (feed == null) return null; holder.feedTitle.setText(feed.getTitle()); + holder.imageView.setContentDescription(feed.getTitle()); holder.feedTitle.setVisibility(View.VISIBLE); int count = itemAccess.getFeedCounter(feed.getId()); if(count > 0) { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java index 55ca5471b..026386cf9 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java @@ -1,16 +1,16 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; +import android.widget.Toast; + import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import android.widget.Toast; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -64,7 +64,7 @@ class DownloadActionButton extends ItemActionButton { private void downloadEpisode(Context context) { try { - DBTasks.downloadFeedItems(context, item); + DownloadRequester.getInstance().downloadMedia(context, item); Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); } catch (DownloadRequestException e) { e.printStackTrace(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java index 31e9fccb5..861c6a4be 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java @@ -10,6 +10,7 @@ import android.widget.ImageButton; 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.storage.DownloadRequester; public abstract class ItemActionButton { @@ -43,6 +44,8 @@ public abstract class ItemActionButton { return new PlayActionButton(item); } else if (isDownloadingMedia) { return new CancelDownloadActionButton(item); + } else if (UserPreferences.streamOverDownload()) { + return new StreamActionButton(item); } else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) { return new DownloadActionButton(item, isInQueue); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java index f8d2a139e..77efd9023 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java @@ -3,15 +3,14 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; import android.widget.Toast; -import com.afollestad.materialdialogs.MaterialDialog; - +import androidx.appcompat.app.AlertDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.storage.DownloadRequester; class MobileDownloadHelper { private static long addToQueueTimestamp; @@ -27,16 +26,15 @@ class MobileDownloadHelper { } static void confirmMobileDownload(final Context context, final FeedItem item) { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context) - .title(R.string.confirm_mobile_download_dialog_title) - .content(R.string.confirm_mobile_download_dialog_message) - .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily)) - .onPositive((dialog, which) -> downloadFeedItems(context, item)); + AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setTitle(R.string.confirm_mobile_download_dialog_title) + .setMessage(R.string.confirm_mobile_download_dialog_message) + .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily), + (dialog, which) -> downloadFeedItems(context, item)); if (!DBReader.getQueueIDList().contains(item.getId())) { - builder - .content(R.string.confirm_mobile_download_dialog_message_not_in_queue) - .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue) - .onNeutral((dialog, which) -> addToQueue(context, item)); + builder.setMessage(R.string.confirm_mobile_download_dialog_message_not_in_queue) + .setNeutralButton(R.string.confirm_mobile_download_dialog_only_add_to_queue, + (dialog, which) -> addToQueue(context, item)); } builder.show(); } @@ -50,7 +48,7 @@ class MobileDownloadHelper { private static void downloadFeedItems(Context context, FeedItem item) { allowMobileDownloadTimestamp = System.currentTimeMillis(); try { - DBTasks.downloadFeedItems(context, item); + DownloadRequester.getInstance().downloadMedia(context, item); Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); } catch (DownloadRequestException e) { e.printStackTrace(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java index 23a7e03ad..0d314b5eb 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java @@ -12,7 +12,6 @@ import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE; -import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE; class PlayActionButton extends ItemActionButton { @@ -52,12 +51,14 @@ class PlayActionButton extends ItemActionButton { } private void togglePlayPause(Context context, FeedMedia media) { - new PlaybackServiceStarter(context, media) - .startWhenPrepared(true) - .shouldStream(false) - .start(); - - String pauseOrResume = media.isCurrentlyPlaying() ? ACTION_PAUSE_PLAY_CURRENT_EPISODE : ACTION_RESUME_PLAY_CURRENT_EPISODE; - IntentUtils.sendLocalBroadcast(context, pauseOrResume); + if (media.isCurrentlyPlaying()) { + IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE); + } else { + new PlaybackServiceStarter(context, media) + .callEvenIfRunning(true) + .startWhenPrepared(true) + .shouldStream(false) + .start(); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java new file mode 100644 index 000000000..c1e619fdf --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java @@ -0,0 +1,64 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; + +import androidx.annotation.AttrRes; +import androidx.annotation.StringRes; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; + +import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE; + +public class StreamActionButton extends ItemActionButton { + + StreamActionButton(FeedItem item) { + super(item); + } + + @Override + @StringRes + public int getLabel() { + return R.string.stream_label; + } + + @Override + @AttrRes + public int getDrawable() { + FeedMedia media = item.getMedia(); + if (media != null && media.isCurrentlyPlaying()) { + return R.attr.av_pause; + } + return R.attr.action_stream; + } + + @Override + public void onClick(Context context) { + final FeedMedia media = item.getMedia(); + if (media == null) { + return; + } + + if (media.isPlaying()) { + togglePlayPause(context, media); + } else { + DBTasks.playMedia(context, media, false, true, true); + } + } + + private void togglePlayPause(Context context, FeedMedia media) { + if (media.isCurrentlyPlaying()) { + IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE); + } else { + new PlaybackServiceStarter(context, media) + .callEvenIfRunning(true) + .startWhenPrepared(true) + .shouldStream(true) + .start(); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java index 2c41b8cb8..073f7b550 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java @@ -1,51 +1,29 @@ package de.danoeh.antennapod.dialog; -import android.app.Dialog; +import android.app.AlertDialog; import android.content.Context; -import android.os.Bundle; import android.view.View; -import android.view.Window; -import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; - import de.danoeh.antennapod.R; /** * Displays a dialog with a username and password text field and an optional checkbox to save username and preferences. */ -public abstract class AuthenticationDialog extends Dialog { - - private final int titleRes; - private final boolean enableUsernameField; - private final boolean showSaveCredentialsCheckbox; - private final String usernameInitialValue; - private final String passwordInitialValue; +public abstract class AuthenticationDialog extends AlertDialog.Builder { - public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField, boolean showSaveCredentialsCheckbox, String usernameInitialValue, String passwordInitialValue) { + public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField, + boolean showSaveCredentialsCheckbox, String usernameInitialValue, + String passwordInitialValue) { super(context); - this.titleRes = titleRes; - this.enableUsernameField = enableUsernameField; - this.showSaveCredentialsCheckbox = showSaveCredentialsCheckbox; - this.usernameInitialValue = usernameInitialValue; - this.passwordInitialValue = passwordInitialValue; - } + setTitle(titleRes); + View rootView = View.inflate(context, R.layout.authentication_dialog, null); + setView(rootView); - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.authentication_dialog); - final EditText etxtUsername = findViewById(R.id.etxtUsername); - final EditText etxtPassword = findViewById(R.id.etxtPassword); - final CheckBox saveUsernamePassword = findViewById(R.id.chkSaveUsernamePassword); - final Button butConfirm = findViewById(R.id.butConfirm); - final Button butCancel = findViewById(R.id.butCancel); + final EditText etxtUsername = rootView.findViewById(R.id.etxtUsername); + final EditText etxtPassword = rootView.findViewById(R.id.etxtPassword); + final CheckBox saveUsernamePassword = rootView.findViewById(R.id.chkSaveUsernamePassword); - if (titleRes != 0) { - setTitle(titleRes); - } else { - requestWindowFeature(Window.FEATURE_NO_TITLE); - } etxtUsername.setEnabled(enableUsernameField); if (showSaveCredentialsCheckbox) { saveUsernamePassword.setVisibility(View.VISIBLE); @@ -59,13 +37,10 @@ public abstract class AuthenticationDialog extends Dialog { etxtPassword.setText(passwordInitialValue); } setOnCancelListener(dialog -> onCancelled()); - butCancel.setOnClickListener(v -> cancel()); - butConfirm.setOnClickListener(v -> { - onConfirmed(etxtUsername.getText().toString(), - etxtPassword.getText().toString(), - showSaveCredentialsCheckbox && saveUsernamePassword.isChecked()); - dismiss(); - }); + setNegativeButton(R.string.cancel_label, null); + setPositiveButton(R.string.confirm_label, (dialog, which) + -> onConfirmed(etxtUsername.getText().toString(), etxtPassword.getText().toString(), + showSaveCredentialsCheckbox && saveUsernamePassword.isChecked())); } protected void onCancelled() { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java index 4cfa7e870..ec285a8f6 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java @@ -2,8 +2,10 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import com.afollestad.materialdialogs.MaterialDialog; - +import android.view.View; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.DataFolderAdapter; @@ -25,21 +27,23 @@ public class ChooseDataFolderDialog { DataFolderAdapter adapter = new DataFolderAdapter(context, handlerFunc); if (adapter.getItemCount() == 0) { - new MaterialDialog.Builder(context) - .title(R.string.error_label) - .content(R.string.external_storage_error_msg) - .neutralText(android.R.string.ok) + new AlertDialog.Builder(context) + .setTitle(R.string.error_label) + .setMessage(R.string.external_storage_error_msg) + .setPositiveButton(android.R.string.ok, null) .show(); return; } - MaterialDialog dialog = new MaterialDialog.Builder(context) - .title(R.string.choose_data_directory) - .content(R.string.choose_data_directory_message) - .adapter(adapter, null) - .negativeText(R.string.cancel_label) - .cancelable(true) - .build(); + View content = View.inflate(context, R.layout.choose_data_folder_dialog, null); + AlertDialog dialog = new AlertDialog.Builder(context) + .setView(content) + .setTitle(R.string.choose_data_directory) + .setMessage(R.string.choose_data_directory_message) + .setNegativeButton(R.string.cancel_label, null) + .create(); + ((RecyclerView) content.findViewById(R.id.recyclerView)).setLayoutManager(new LinearLayoutManager(context)); + ((RecyclerView) content.findViewById(R.id.recyclerView)).setAdapter(adapter); adapter.setDialog(dialog); dialog.show(); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java index f6d08b7bf..ff131aeba 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -4,16 +4,6 @@ import android.app.AlertDialog; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Bundle; -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.PluralsRes; -import androidx.annotation.StringRes; -import com.google.android.material.snackbar.Snackbar; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; -import androidx.collection.ArrayMap; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -24,6 +14,17 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.PluralsRes; +import androidx.annotation.StringRes; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.collection.ArrayMap; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.Fragment; + +import com.google.android.material.snackbar.Snackbar; import com.leinardi.android.speeddial.SpeedDialView; import java.util.ArrayList; @@ -35,10 +36,12 @@ import java.util.Map; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; +import de.danoeh.antennapod.core.storage.DownloadRequester; +import de.danoeh.antennapod.core.util.FeedItemPermutors; import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.core.util.SortOrder; public class EpisodesApplyActionFragment extends Fragment { @@ -263,6 +266,18 @@ public class EpisodesApplyActionFragment extends Fragment { mSelectToggle.setTitle(titleResId); } + private static final Map<Integer, SortOrder> menuItemIdToSortOrder; + static { + Map<Integer, SortOrder> map = new ArrayMap<>(); + map.put(R.id.sort_title_a_z, SortOrder.EPISODE_TITLE_A_Z); + map.put(R.id.sort_title_z_a, SortOrder.EPISODE_TITLE_Z_A); + map.put(R.id.sort_date_new_old, SortOrder.DATE_NEW_OLD); + map.put(R.id.sort_date_old_new, SortOrder.DATE_OLD_NEW); + map.put(R.id.sort_duration_long_short, SortOrder.DURATION_LONG_SHORT); + map.put(R.id.sort_duration_short_long, SortOrder.DURATION_SHORT_LONG); + menuItemIdToSortOrder = Collections.unmodifiableMap(map); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { @StringRes int resId = 0; @@ -305,24 +320,12 @@ public class EpisodesApplyActionFragment extends Fragment { checkWithMedia(); resId = R.string.selected_has_media_label; break; - case R.id.sort_title_a_z: - sortByTitle(false); - return true; - case R.id.sort_title_z_a: - sortByTitle(true); - return true; - case R.id.sort_date_new_old: - sortByDate(true); - return true; - case R.id.sort_date_old_new: - sortByDate(false); - return true; - case R.id.sort_duration_long_short: - sortByDuration(true); - return true; - case R.id.sort_duration_short_long: - sortByDuration(false); - return true; + default: // handle various sort options + SortOrder sortOrder = menuItemIdToSortOrder.get(item.getItemId()); + if (sortOrder != null) { + sort(sortOrder); + return true; + } } if(resId != 0) { Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT) @@ -333,52 +336,9 @@ public class EpisodesApplyActionFragment extends Fragment { } } - private void sortByTitle(final boolean reverse) { - Collections.sort(episodes, (lhs, rhs) -> { - if (reverse) { - return -1 * lhs.getTitle().compareTo(rhs.getTitle()); - } else { - return lhs.getTitle().compareTo(rhs.getTitle()); - } - }); - refreshTitles(); - refreshCheckboxes(); - } - - private void sortByDate(final boolean reverse) { - Collections.sort(episodes, (lhs, rhs) -> { - if (lhs.getPubDate() == null) { - return -1; - } else if (rhs.getPubDate() == null) { - return 1; - } - int code = lhs.getPubDate().compareTo(rhs.getPubDate()); - if (reverse) { - return -1 * code; - } else { - return code; - } - }); - refreshTitles(); - refreshCheckboxes(); - } - - private void sortByDuration(final boolean reverse) { - Collections.sort(episodes, (lhs, rhs) -> { - int ordering; - if (!lhs.hasMedia()) { - ordering = 1; - } else if (!rhs.hasMedia()) { - ordering = -1; - } else { - ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration(); - } - if(reverse) { - return -1 * ordering; - } else { - return ordering; - } - }); + private void sort(@NonNull SortOrder sortOrder) { + FeedItemPermutors.getPermutor(sortOrder) + .reorder(episodes); refreshTitles(); refreshCheckboxes(); } @@ -525,7 +485,7 @@ public class EpisodesApplyActionFragment extends Fragment { } } try { - DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()])); + DownloadRequester.getInstance().downloadMedia(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()])); } catch (DownloadRequestException e) { e.printStackTrace(); DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java new file mode 100644 index 000000000..2ee716c7c --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java @@ -0,0 +1,51 @@ +package de.danoeh.antennapod.dialog; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.util.SortOrder; + +public abstract class IntraFeedSortDialog { + + @Nullable + protected SortOrder currentSortOrder; + @NonNull + protected Context context; + + public IntraFeedSortDialog(@NonNull Context context, @Nullable SortOrder sortOrder) { + this.context = context; + this.currentSortOrder = sortOrder; + } + + public void openDialog() { + final String[] items = context.getResources().getStringArray(R.array.feed_episodes_sort_options); + final String[] valueStrs = context.getResources().getStringArray(R.array.feed_episodes_sort_values); + final SortOrder[] values = new SortOrder[valueStrs.length]; + for (int i = 0; i < valueStrs.length; i++) { + values[i] = SortOrder.valueOf(valueStrs[i]); + } + + int idxCurrentSort = -1; + for (int i = 0; i < values.length; i++) { + if (currentSortOrder == values[i]) { + idxCurrentSort = i; + break; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.sort); + builder.setSingleChoiceItems(items, idxCurrentSort, (dialog, idxNewSort) -> { + updateSort(values[idxNewSort]); + dialog.dismiss(); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } + + protected abstract void updateSort(@NonNull SortOrder sortOrder); +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java index f53dbe57a..3e4e40a5b 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -3,15 +3,16 @@ package de.danoeh.antennapod.dialog; import android.app.Dialog; import android.os.Bundle; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; import android.widget.Button; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.TextView; -import com.afollestad.materialdialogs.MaterialDialog; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import java.util.Locale; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; +import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.playback.Playable; @@ -24,7 +25,7 @@ public class PlaybackControlsDialog extends DialogFragment { private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo"; private PlaybackController controller; - private MaterialDialog dialog; + private AlertDialog dialog; private boolean isPlayingVideo; public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) { @@ -42,7 +43,12 @@ public class PlaybackControlsDialog extends DialogFragment { @Override public void onStart() { super.onStart(); - controller = new PlaybackController(getActivity(), false); + controller = new PlaybackController(getActivity(), false) { + @Override + public void setupGUI() { + setupUi(); + } + }; controller.init(); setupUi(); } @@ -59,15 +65,14 @@ public class PlaybackControlsDialog extends DialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO); - dialog = new MaterialDialog.Builder(getContext()) - .title(R.string.audio_controls) - .customView(R.layout.audio_controls, true) - .neutralText(R.string.close_label) - .onNeutral((dialog1, which) -> { - final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left); - final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right); + dialog = new AlertDialog.Builder(getContext()) + .setTitle(R.string.audio_controls) + .setView(R.layout.audio_controls) + .setPositiveButton(R.string.close_label, (dialog1, which) -> { + final SeekBar left = dialog.findViewById(R.id.volume_left); + final SeekBar right = dialog.findViewById(R.id.volume_right); UserPreferences.setVolume(left.getProgress(), right.getProgress()); - }).build(); + }).create(); return dialog; } @@ -110,6 +115,7 @@ public class PlaybackControlsDialog extends DialogFragment { controller.setPlaybackSpeed(playbackSpeed); String speedPref = String.format(Locale.US, "%.2f", playbackSpeed); + PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed); if (isPlayingVideo) { UserPreferences.setVideoPlaybackSpeed(speedPref); } else { @@ -136,7 +142,7 @@ public class PlaybackControlsDialog extends DialogFragment { public void onStopTrackingTouch(SeekBar seekBar) { } }); - barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)); + barPlaybackSpeed.setProgress(Math.round((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP)); final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left); barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage()); @@ -212,6 +218,6 @@ public class PlaybackControlsDialog extends DialogFragment { media = controller.getMedia(); } - return PlaybackSpeedHelper.getCurrentPlaybackSpeed(media); + return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media); } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java index 0499d02f1..11256f2de 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java @@ -4,6 +4,7 @@ import android.app.Dialog; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; +import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import android.text.Editable; import android.text.TextUtils; @@ -16,10 +17,6 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import com.afollestad.materialdialogs.DialogAction; -import com.afollestad.materialdialogs.MaterialDialog; -import com.afollestad.materialdialogs.internal.MDButton; - import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; @@ -48,7 +45,7 @@ public class ProxyDialog { private final Context context; - private MaterialDialog dialog; + private AlertDialog dialog; private Spinner spType; private EditText etHost; @@ -64,54 +61,53 @@ public class ProxyDialog { this.context = context; } - public Dialog createDialog() { - dialog = new MaterialDialog.Builder(context) - .title(R.string.pref_proxy_title) - .customView(R.layout.proxy_settings, true) - .positiveText(R.string.proxy_test_label) - .negativeText(R.string.cancel_label) - .onPositive((dialog1, which) -> { - if(!testSuccessful) { - dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false); - test(); - return; - } - String type = (String) ((Spinner) dialog1.findViewById(R.id.spType)).getSelectedItem(); - ProxyConfig proxy; - if(Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) { - proxy = ProxyConfig.direct(); - } else { - String host = etHost.getText().toString(); - String port = etPort.getText().toString(); - String username = etUsername.getText().toString(); - if(TextUtils.isEmpty(username)) { - username = null; - } - String password = etPassword.getText().toString(); - if(TextUtils.isEmpty(password)) { - password = null; - } - int portValue = 0; - if(!TextUtils.isEmpty(port)) { - portValue = Integer.valueOf(port); - } - if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) { - proxy = ProxyConfig.socks(host, portValue, username, password); - } else { - proxy = ProxyConfig.http(host, portValue, username, password); - } - } - UserPreferences.setProxyConfig(proxy); - AntennapodHttpClient.reinit(); - dialog.dismiss(); - }) - .onNegative((dialog1, which) -> dialog1.dismiss()) - .autoDismiss(false) - .build(); - View view = dialog.getCustomView(); - spType = view.findViewById(R.id.spType); + public Dialog show() { + View content = View.inflate(context, R.layout.proxy_settings, null); + dialog = new AlertDialog.Builder(context) + .setTitle(R.string.pref_proxy_title) + .setView(content) + .setNegativeButton(R.string.cancel_label, null) + .setPositiveButton(R.string.proxy_test_label, null) + .show(); + // To prevent cancelling the dialog on button click + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> { + if (!testSuccessful) { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + test(); + return; + } + String type = (String) ((Spinner) content.findViewById(R.id.spType)).getSelectedItem(); + ProxyConfig proxy; + if (Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) { + proxy = ProxyConfig.direct(); + } else { + String host = etHost.getText().toString(); + String port = etPort.getText().toString(); + String username = etUsername.getText().toString(); + if(TextUtils.isEmpty(username)) { + username = null; + } + String password = etPassword.getText().toString(); + if(TextUtils.isEmpty(password)) { + password = null; + } + int portValue = 0; + if(!TextUtils.isEmpty(port)) { + portValue = Integer.valueOf(port); + } + if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) { + proxy = ProxyConfig.socks(host, portValue, username, password); + } else { + proxy = ProxyConfig.http(host, portValue, username, password); + } + } + UserPreferences.setProxyConfig(proxy); + AntennapodHttpClient.reinit(); + dialog.dismiss(); + }); - List<String> types= new ArrayList<>(); + spType = content.findViewById(R.id.spType); + List<String> types = new ArrayList<>(); types.add(Proxy.Type.DIRECT.name()); types.add(Proxy.Type.HTTP.name()); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -123,22 +119,22 @@ public class ProxyDialog { spType.setAdapter(adapter); ProxyConfig proxyConfig = UserPreferences.getProxyConfig(); spType.setSelection(adapter.getPosition(proxyConfig.type.name())); - etHost = view.findViewById(R.id.etHost); + etHost = content.findViewById(R.id.etHost); if(!TextUtils.isEmpty(proxyConfig.host)) { etHost.setText(proxyConfig.host); } etHost.addTextChangedListener(requireTestOnChange); - etPort = view.findViewById(R.id.etPort); + etPort = content.findViewById(R.id.etPort); if(proxyConfig.port > 0) { etPort.setText(String.valueOf(proxyConfig.port)); } etPort.addTextChangedListener(requireTestOnChange); - etUsername = view.findViewById(R.id.etUsername); + etUsername = content.findViewById(R.id.etUsername); if(!TextUtils.isEmpty(proxyConfig.username)) { etUsername.setText(proxyConfig.username); } etUsername.addTextChangedListener(requireTestOnChange); - etPassword = view.findViewById(R.id.etPassword); + etPassword = content.findViewById(R.id.etPassword); if(!TextUtils.isEmpty(proxyConfig.password)) { etPassword.setText(proxyConfig.username); } @@ -159,7 +155,7 @@ public class ProxyDialog { enableSettings(false); } }); - txtvMessage = view.findViewById(R.id.txtvMessage); + txtvMessage = content.findViewById(R.id.txtvMessage); checkValidity(); return dialog; } @@ -230,14 +226,12 @@ public class ProxyDialog { private void setTestRequired(boolean required) { if(required) { testSuccessful = false; - MDButton button = dialog.getActionButton(DialogAction.POSITIVE); - button.setText(context.getText(R.string.proxy_test_label)); - button.setEnabled(true); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); } else { testSuccessful = true; - MDButton button = dialog.getActionButton(DialogAction.POSITIVE); - button.setText(context.getText(android.R.string.ok)); - button.setEnabled(true); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java index c49e9153e..7cb274708 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java @@ -7,11 +7,10 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import android.util.Log; -import com.afollestad.materialdialogs.MaterialDialog; - import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; +import androidx.appcompat.app.AlertDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.util.IntentUtils; @@ -97,21 +96,18 @@ public class RatingDialog { } @Nullable - private static MaterialDialog createDialog() { + private static AlertDialog createDialog() { Context context = mContext.get(); - if(context == null) { + if (context == null) { return null; } - return new MaterialDialog.Builder(context) - .title(R.string.rating_title) - .content(R.string.rating_message) - .positiveText(R.string.rating_now_label) - .negativeText(R.string.rating_never_label) - .neutralText(R.string.rating_later_label) - .onPositive((dialog, which) -> rateNow()) - .onNegative((dialog, which) -> saveRated()) - .onNeutral((dialog, which) -> resetStartDate()) - .cancelListener(dialog1 -> resetStartDate()) - .build(); + return new AlertDialog.Builder(context) + .setTitle(R.string.rating_title) + .setMessage(R.string.rating_message) + .setPositiveButton(R.string.rating_now_label, (dialog, which) -> rateNow()) + .setNegativeButton(R.string.rating_never_label, (dialog, which) -> saveRated()) + .setNeutralButton(R.string.rating_later_label, (dialog, which) -> resetStartDate()) + .setOnCancelListener(dialog1 -> resetStartDate()) + .create(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java index 31a544582..699c6f492 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java @@ -3,10 +3,12 @@ package de.danoeh.antennapod.dialog; import android.app.Activity; import android.text.InputType; -import com.afollestad.materialdialogs.MaterialDialog; - import java.lang.ref.WeakReference; +import android.view.View; +import android.widget.EditText; +import androidx.appcompat.app.AlertDialog; +import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.storage.DBWriter; @@ -25,20 +27,24 @@ public class RenameFeedDialog { if(activity == null) { return; } - new MaterialDialog.Builder(activity) - .title(de.danoeh.antennapod.core.R.string.rename_feed_label) - .inputType(InputType.TYPE_CLASS_TEXT) - .input(feed.getTitle(), feed.getTitle(), true, (dialog, input) -> { - feed.setCustomTitle(input.toString()); + + View content = View.inflate(activity, R.layout.edit_text_dialog, null); + EditText editText = content.findViewById(R.id.text); + editText.setText(feed.getTitle()); + AlertDialog dialog = new AlertDialog.Builder(activity) + .setView(content) + .setTitle(de.danoeh.antennapod.core.R.string.rename_feed_label) + .setPositiveButton(android.R.string.ok, (d, input) -> { + feed.setCustomTitle(editText.getText().toString()); DBWriter.setFeedCustomTitle(feed); - dialog.dismiss(); }) - .neutralText(de.danoeh.antennapod.core.R.string.reset) - .onNeutral((dialog, which) -> dialog.getInputEditText().setText(feed.getFeedTitle())) - .negativeText(de.danoeh.antennapod.core.R.string.cancel_label) - .onNegative((dialog, which) -> dialog.dismiss()) - .autoDismiss(false) + .setNeutralButton(de.danoeh.antennapod.core.R.string.reset, null) + .setNegativeButton(de.danoeh.antennapod.core.R.string.cancel_label, null) .show(); + + // To prevent cancelling the dialog on button click + dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener( + (view) -> editText.setText(feed.getFeedTitle())); } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java index dc056a3f9..8d176c708 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java @@ -3,7 +3,6 @@ package de.danoeh.antennapod.dialog; import android.content.Context; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; @@ -12,9 +11,7 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; -import com.afollestad.materialdialogs.DialogAction; -import com.afollestad.materialdialogs.MaterialDialog; - +import androidx.appcompat.app.AlertDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; @@ -26,7 +23,7 @@ public abstract class SleepTimerDialog { private final Context context; - private MaterialDialog dialog; + private AlertDialog dialog; private EditText etxtTime; private Spinner spTimeUnit; private CheckBox cbShakeToReset; @@ -38,40 +35,38 @@ public abstract class SleepTimerDialog { this.context = context; } - public MaterialDialog createNewDialog() { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder.title(R.string.set_sleeptimer_label); - builder.customView(R.layout.time_dialog, false); - builder.positiveText(R.string.set_sleeptimer_label); - builder.negativeText(R.string.cancel_label); - builder.onNegative((dialog, which) -> dialog.dismiss()); - builder.onPositive((dialog, which) -> { - try { - savePreferences(); - long input = SleepTimerPreferences.timerMillis(); - onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked()); - dialog.dismiss(); - } catch (NumberFormatException e) { - e.printStackTrace(); - Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input, - Toast.LENGTH_LONG); - toast.show(); - } - }); - dialog = builder.build(); - - View view = dialog.getView(); - etxtTime = view.findViewById(R.id.etxtTime); - spTimeUnit = view.findViewById(R.id.spTimeUnit); - cbShakeToReset = view.findViewById(R.id.cbShakeToReset); - cbVibrate = view.findViewById(R.id.cbVibrate); - chAutoEnable = view.findViewById(R.id.chAutoEnable); + public AlertDialog createNewDialog() { + View content = View.inflate(context, R.layout.time_dialog, null); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.set_sleeptimer_label); + builder.setView(content); + builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> dialog.dismiss()); + builder.setPositiveButton(R.string.set_sleeptimer_label, (dialog, which) -> { + try { + savePreferences(); + long input = SleepTimerPreferences.timerMillis(); + onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked()); + dialog.dismiss(); + } catch (NumberFormatException e) { + e.printStackTrace(); + Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input, + Toast.LENGTH_LONG); + toast.show(); + } + }); + dialog = builder.create(); + + etxtTime = content.findViewById(R.id.etxtTime); + spTimeUnit = content.findViewById(R.id.spTimeUnit); + cbShakeToReset = content.findViewById(R.id.cbShakeToReset); + cbVibrate = content.findViewById(R.id.cbVibrate); + chAutoEnable = content.findViewById(R.id.chAutoEnable); etxtTime.setText(SleepTimerPreferences.lastTimerValue()); etxtTime.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable s) { - checkInputLength(s.length()); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0); } @Override @@ -109,16 +104,6 @@ public abstract class SleepTimerDialog { return dialog; } - private void checkInputLength(int length) { - if (length > 0) { - Log.d(TAG, "Length is larger than 0, enabling confirm button"); - dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true); - } else { - Log.d(TAG, "Length is smaller than 0, disabling confirm button"); - dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false); - } - } - public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate); private void savePreferences() { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 1cf34b2b3..ef624ebe6 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -1,37 +1,21 @@ package de.danoeh.antennapod.dialog; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.Intent; -import android.net.Uri; import android.os.Build; import androidx.appcompat.app.AlertDialog; -import android.util.Log; -import android.view.View; - -import com.afollestad.materialdialogs.DialogAction; -import com.afollestad.materialdialogs.MaterialDialog; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; import java.util.Arrays; import java.util.List; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.IntentUtils; - public class VariableSpeedDialog { - private static final String TAG = VariableSpeedDialog.class.getSimpleName(); - - private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse("market://details?id=com.falconware.prestissimo")); - private VariableSpeedDialog() { } public static void showDialog(final Context context) { - if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context) - || UserPreferences.useSonic() + if (UserPreferences.useSonic() || UserPreferences.useExoplayer() || Build.VERSION.SDK_INT >= 23) { showSpeedSelectorDialog(context); @@ -45,38 +29,17 @@ public class VariableSpeedDialog { } private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder.title(R.string.no_playback_plugin_title); - builder.content(R.string.no_playback_plugin_or_sonic_msg); - builder.positiveText(R.string.enable_sonic); - builder.negativeText(R.string.download_plugin_label); - builder.neutralText(R.string.close_label); - builder.onPositive((dialog, which) -> { - if (Build.VERSION.SDK_INT >= 16) { // just to be safe - UserPreferences.enableSonic(); - if(showSpeedSelector) { - showSpeedSelectorDialog(context); - } - } - }); - builder.onNegative((dialog, which) -> { - try { - context.startActivity(playStoreIntent); - } catch (ActivityNotFoundException e) { - // this is usually thrown on an emulator if the Android market is not installed - Log.e(TAG, Log.getStackTraceString(e)); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.no_playback_plugin_title); + builder.setMessage(R.string.no_playback_plugin_or_sonic_msg); + builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> { + UserPreferences.enableSonic(); + if (showSpeedSelector) { + showSpeedSelectorDialog(context); } }); - builder.forceStacking(true); - MaterialDialog dialog = builder.show(); - if (Build.VERSION.SDK_INT < 16) { - View pos = dialog.getActionButton(DialogAction.POSITIVE); - pos.setEnabled(false); - } - if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) { - View pos = dialog.getActionButton(DialogAction.NEGATIVE); - pos.setEnabled(false); - } + builder.setNeutralButton(R.string.close_label, null); + builder.show(); } private static void showSpeedSelectorDialog(final Context context) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index b7bfe3438..2cfe7c1e8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -46,7 +46,7 @@ public class AddFeedFragment extends Fragment { View butOpmlImport = root.findViewById(R.id.btn_opml_import); butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(), OpmlImportFromPathActivity.class))); - + root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch()); return root; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java index 5ab781fe3..47d7a0b86 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java @@ -101,7 +101,6 @@ public class CombinedSearchFragment extends Fragment { inflater.inflate(R.menu.itunes_search, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_label)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index 1917e4c75..7f70daaec 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -16,7 +16,8 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter; -import de.danoeh.antennapod.core.feed.EventDistributor; +import de.danoeh.antennapod.core.event.DownloadLogEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; @@ -27,6 +28,8 @@ 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 static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE; import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE; @@ -38,10 +41,6 @@ public class CompletedDownloadsFragment extends ListFragment { private static final String TAG = CompletedDownloadsFragment.class.getSimpleName(); - private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | - EventDistributor.DOWNLOADLOG_UPDATE | - EventDistributor.UNREAD_ITEMS_UPDATE; - private List<FeedItem> items = new ArrayList<>(); private DownloadedEpisodesListAdapter listAdapter; private Disposable disposable; @@ -56,19 +55,24 @@ public class CompletedDownloadsFragment extends ListFragment { listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess); setListAdapter(listAdapter); setListShown(false); + EventBus.getDefault().register(this); + } + + @Override + public void onDestroyView() { + EventBus.getDefault().unregister(this); + super.onDestroyView(); } @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); loadItems(); } @Override public void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); if (disposable != null) { disposable.dispose(); } @@ -79,7 +83,7 @@ public class CompletedDownloadsFragment extends ListFragment { super.onListItemClick(l, v, position, id); position -= l.getHeaderViewsCount(); long[] ids = FeedItemUtil.getIds(items); - ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position)); + ((MainActivity) requireActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position)); } @Override @@ -135,14 +139,15 @@ public class CompletedDownloadsFragment extends ListFragment { } }; - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - loadItems(); - } - } - }; + @Subscribe + public void onDownloadLogChanged(DownloadLogEvent event) { + loadItems(); + } + + @Subscribe + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + loadItems(); + } private void loadItems() { if (disposable != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index cf9ee6c41..5467d71a8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -15,6 +15,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import io.reactivex.Maybe; @@ -70,7 +71,7 @@ public class CoverFragment extends Fragment { txtvPodcastTitle.setText(media.getFeedTitle()); txtvEpisodeTitle.setText(media.getEpisodeTitle()); Glide.with(this) - .load(media.getImageLocation()) + .load(ImageResourceUtils.getImageLocation(media)) .apply(new RequestOptions() .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) .dontAnimate() diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index 528c50747..16337b00d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -19,7 +19,7 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.DownloadLogAdapter; -import de.danoeh.antennapod.core.feed.EventDistributor; +import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.DownloadStatus; @@ -30,6 +30,8 @@ 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; /** * Shows the download log @@ -45,14 +47,12 @@ public class DownloadLogFragment extends ListFragment { @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); loadItems(); } @Override public void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); if (disposable != null) { disposable.dispose(); } @@ -77,6 +77,13 @@ public class DownloadLogFragment extends ListFragment { adapter = new DownloadLogAdapter(getActivity(), itemAccess); setListAdapter(adapter); + EventBus.getDefault().register(this); + } + + @Override + public void onDestroyView() { + EventBus.getDefault().unregister(this); + super.onDestroyView(); } private void onFragmentLoaded() { @@ -133,15 +140,10 @@ public class DownloadLogFragment extends ListFragment { } }; - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) { - loadItems(); - } - } - }; + @Subscribe + public void onDownloadLogChanged(DownloadLogEvent event) { + loadItems(); + } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 3fc67f795..5dbb703b7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -24,7 +24,10 @@ import android.widget.Toast; import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; +import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import de.danoeh.antennapod.core.event.PlayerStatusEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -39,7 +42,6 @@ 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.event.FeedItemEvent; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.DownloadRequest; @@ -64,11 +66,6 @@ import io.reactivex.schedulers.Schedulers; public abstract class EpisodesListFragment extends Fragment { public static final String TAG = "EpisodesListFragment"; - - private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE | - EventDistributor.UNREAD_ITEMS_UPDATE | - EventDistributor.PLAYER_STATUS_UPDATE; - private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment"; private static final String PREF_SCROLL_POSITION = "scroll_position"; private static final String PREF_SCROLL_OFFSET = "scroll_offset"; @@ -102,7 +99,6 @@ public abstract class EpisodesListFragment extends Fragment { public void onStart() { super.onStart(); setHasOptionsMenu(true); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadItems(); } @@ -124,7 +120,6 @@ public abstract class EpisodesListFragment extends Fragment { public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); - EventDistributor.getInstance().unregister(contentUpdate); if (disposable != null) { disposable.dispose(); } @@ -174,7 +169,6 @@ public abstract class EpisodesListFragment extends Fragment { MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override @@ -388,7 +382,14 @@ public abstract class EpisodesListFragment extends Fragment { @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(PlaybackPositionEvent event) { if (listAdapter != null) { - listAdapter.notifyCurrentlyPlayingItemChanged(event); + for (int i = 0; i < listAdapter.getItemCount(); i++) { + AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) + recyclerView.findViewHolderForAdapterPosition(i); + if (holder != null && holder.isCurrentlyPlayingItem()) { + holder.notifyPlaybackPositionUpdated(event); + break; + } + } } } @@ -414,17 +415,27 @@ public abstract class EpisodesListFragment extends Fragment { } } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - loadItems(); - if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { - requireActivity().invalidateOptionsMenu(); - } - } + private void updateUi() { + loadItems(); + if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { + requireActivity().invalidateOptionsMenu(); } - }; + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onPlayerStatusChanged(PlayerStatusEvent event) { + updateUi(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + updateUi(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onFeedListChanged(FeedListUpdateEvent event) { + updateUi(); + } void loadItems() { if (disposable != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index bbc33c6ca..ce2232a55 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -22,6 +22,7 @@ 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; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import io.reactivex.Maybe; @@ -70,9 +71,9 @@ public class ExternalPlayerFragment extends Fragment { if (controller != null && controller.getMedia() != null) { Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia()); - if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) { - ActivityOptionsCompat options = ActivityOptionsCompat. - makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition"); + if (controller.getMedia().getMediaType() == MediaType.AUDIO) { + ActivityOptionsCompat options = ActivityOptionsCompat + .makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition"); startActivity(intent, options.toBundle()); } else { startActivity(intent); @@ -227,7 +228,7 @@ public class ExternalPlayerFragment extends Fragment { onPositionObserverUpdate(); Glide.with(getActivity()) - .load(media.getImageLocation()) + .load(ImageResourceUtils.getImageLocation(media)) .apply(new RequestOptions() .placeholder(R.color.light_gray) .error(R.color.light_gray) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 5282a6bb2..f73735658 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -54,7 +54,8 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment { emptyView.setTitle(R.string.no_fav_episodes_head_label); emptyView.setMessage(R.string.no_fav_episodes_label); - ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { + ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, + ItemTouchHelper.LEFT) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java index 6b270e220..79637d79a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java @@ -167,16 +167,8 @@ public class FeedInfoFragment extends Fragment { 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 = ""; - } + String description = HtmlToPlainText.getPlainText(feed.getDescription()); + txtvDescription.setText(description); if (!TextUtils.isEmpty(feed.getAuthor())) { @@ -222,6 +214,10 @@ public class FeedInfoFragment extends Fragment { @Override public boolean onOptionsItemSelected(MenuItem item) { + if (feed == null) { + Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show(); + return super.onOptionsItemSelected(item); + } boolean handled = false; try { handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java index 0c33dce5a..33343948f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -5,10 +5,6 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.LightingColorFilter; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.ListFragment; -import androidx.core.view.MenuItemCompat; -import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -23,12 +19,17 @@ import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.SearchView; +import androidx.core.view.MenuItemCompat; +import androidx.fragment.app.ListFragment; + import com.bumptech.glide.Glide; 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; @@ -45,7 +46,12 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; 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.event.FeedListUpdateEvent; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import de.danoeh.antennapod.core.event.PlayerStatusEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; + import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedEvent; import de.danoeh.antennapod.core.feed.FeedItem; @@ -59,6 +65,7 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; +import de.danoeh.antennapod.core.util.FeedItemPermutors; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.Optional; @@ -79,12 +86,6 @@ import io.reactivex.schedulers.Schedulers; @SuppressLint("ValidFragment") public class FeedItemlistFragment extends ListFragment { private static final String TAG = "ItemlistFragment"; - - private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE - | EventDistributor.FEED_LIST_UPDATE - | EventDistributor.PLAYER_STATUS_UPDATE; - - public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem"; private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; private FeedItemlistAdapter adapter; @@ -151,7 +152,6 @@ public class FeedItemlistFragment extends ListFragment { registerForContextMenu(getListView()); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadItems(); } @@ -160,7 +160,6 @@ public class FeedItemlistFragment extends ListFragment { public void onDestroyView() { super.onDestroyView(); - EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); if (disposable != null) { disposable.dispose(); @@ -187,11 +186,11 @@ public class FeedItemlistFragment extends ListFragment { MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_hint)); searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { + menu.findItem(R.id.sort_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.findItem(R.id.filter_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.findItem(R.id.episode_actions).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.findItem(R.id.refresh_item).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); @@ -237,6 +236,10 @@ public class FeedItemlistFragment extends ListFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { if (!super.onOptionsItemSelected(item)) { + if (feed == null) { + Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show(); + return true; + } try { if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) { switch (item.getItemId()) { @@ -345,7 +348,7 @@ public class FeedItemlistFragment extends ListFragment { position -= l.getHeaderViewsCount(); MainActivity activity = (MainActivity) getActivity(); long[] ids = FeedItemUtil.getIds(feed.getItems()); - activity.loadChildFragment(ItemFragment.newInstance(ids, position)); + activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); activity.getSupportActionBar().setTitle(feed.getTitle()); } @@ -392,18 +395,28 @@ public class FeedItemlistFragment extends ListFragment { } } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private void updateUi() { + refreshHeaderView(); + loadItems(); + updateProgressBarVisibility(); + } - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EVENTS & arg) != 0) { - Log.d(TAG, "Received contentUpdate Intent. arg " + arg); - refreshHeaderView(); - loadItems(); - updateProgressBarVisibility(); - } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onPlayerStatusChanged(PlayerStatusEvent event) { + updateUi(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + updateUi(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onFeedListChanged(FeedListUpdateEvent event) { + if (event.contains(feed)) { + updateUi(); } - }; + } private void updateProgressBarVisibility() { if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) { @@ -629,6 +642,11 @@ public class FeedItemlistFragment extends ListFragment { FeedItemFilter filter = feed.getItemFilter(); feed.setItems(filter.filter(feed.getItems())); } + if (feed != null && feed.getSortOrder() != null) { + List<FeedItem> feedItems = feed.getItems(); + FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems); + feed.setItems(feedItems); + } return Optional.ofNullable(feed); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java index a3c3df340..aa26610aa 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java @@ -100,7 +100,6 @@ public class FyydSearchFragment extends Fragment { inflater.inflate(R.menu.itunes_search, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_fyyd_label)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index a97d60099..85978b761 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -144,7 +144,6 @@ public class ItemDescriptionFragment extends Fragment { } }; - @SuppressWarnings("deprecation") @SuppressLint("NewApi") @Override public boolean onContextItemSelected(MenuItem item) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java index f17f8c645..2806f48ba 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -3,55 +3,49 @@ package de.danoeh.antennapod.fragment; import android.content.ClipData; import android.content.Context; import android.content.Intent; +import android.content.res.TypedArray; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.core.content.ContextCompat; -import androidx.core.view.GestureDetectorCompat; import android.text.Layout; import android.text.TextUtils; import android.util.Log; +import android.util.TypedValue; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.Button; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; - +import androidx.annotation.AttrRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconButton; - -import org.apache.commons.lang3.ArrayUtils; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.util.List; - import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.CastEnabledActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; 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.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.Downloader; @@ -61,63 +55,45 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; -import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.playback.Timeline; -import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; -import de.danoeh.antennapod.view.OnSwipeGesture; -import de.danoeh.antennapod.view.SwipeGestureDetector; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.apache.commons.lang3.ArrayUtils; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.List; /** * Displays information about a FeedItem and actions. */ -public class ItemFragment extends Fragment implements OnSwipeGesture { +public class ItemFragment extends Fragment { private static final String TAG = "ItemFragment"; - - private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE; - - private static final String ARG_FEEDITEMS = "feeditems"; - private static final String ARG_FEEDITEM_POS = "feeditem_pos"; - - private GestureDetectorCompat headerGestureDetector; - private GestureDetectorCompat webviewGestureDetector; + private static final String ARG_FEEDITEM = "feeditem"; /** * Creates a new instance of an ItemFragment * - * @param feeditem The ID of the FeedItem that should be displayed. + * @param feeditem The ID of the FeedItem to show * @return The ItemFragment instance */ public static ItemFragment newInstance(long feeditem) { - return newInstance(new long[] { feeditem }, 0); - } - - /** - * Creates a new instance of an ItemFragment - * - * @param feeditems The IDs of the FeedItems that belong to the same list - * @param feedItemPos The position of the FeedItem that is currently shown - * @return The ItemFragment instance - */ - public static ItemFragment newInstance(long[] feeditems, int feedItemPos) { ItemFragment fragment = new ItemFragment(); Bundle args = new Bundle(); - args.putLongArray(ARG_FEEDITEMS, feeditems); - args.putInt(ARG_FEEDITEM_POS, feedItemPos); + args.putLong(ARG_FEEDITEM, feeditem); fragment.setArguments(args); return fragment; } private boolean itemsLoaded = false; - private long[] feedItems; - private int feedItemPos; + private long itemId; private FeedItem item; private String webviewData; private List<Downloader> downloaderList; @@ -131,9 +107,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { private ImageView imgvCover; private ProgressBar progbarDownload; private ProgressBar progbarLoading; - private IconButton butAction1; - private IconButton butAction2; - private Menu popupMenu; + private Button butAction1; + private Button butAction2; private Disposable disposable; @@ -145,20 +120,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setRetainInstance(true); - setHasOptionsMenu(true); - - feedItems = getArguments().getLongArray(ARG_FEEDITEMS); - feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS); - headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this)); - webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) { - // necessary for the longclick context menu to work properly - @Override - public boolean onDown(MotionEvent e) { - return false; - } - }); + itemId = getArguments().getLong(ARG_FEEDITEM); } @Override @@ -168,11 +131,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { root = layout.findViewById(R.id.content_root); - LinearLayout header = root.findViewById(R.id.header); - if(feedItems.length > 0) { - header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event)); - } - txtvPodcast = layout.findViewById(R.id.txtvPodcast); txtvPodcast.setOnClickListener(v -> openPodcast()); txtvTitle = layout.findViewById(R.id.txtvTitle); @@ -203,10 +161,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { webvDescription.getSettings().setLayoutAlgorithm( WebSettings.LayoutAlgorithm.NARROW_COLUMNS); webvDescription.getSettings().setLoadWithOverviewMode(true); - if(feedItems.length > 0) { webvDescription.setOnLongClickListener(webViewLongClickListener); - } - webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event)); + webvDescription.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { @@ -260,6 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + load(); + } + + @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); } @@ -267,15 +229,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); - load(); } @Override public void onResume() { super.onResume(); - if(itemsLoaded) { + if (itemsLoaded) { progbarLoading.setVisibility(View.GONE); updateAppearance(); } @@ -284,7 +244,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Override public void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); } @@ -300,68 +259,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } } - @Override - public boolean onSwipeLeftToRight() { - return swipeFeedItem(-1); - } - - @Override - public boolean onSwipeRightToLeft() { - return swipeFeedItem(+1); - } - - private boolean swipeFeedItem(int position) { - Log.d(TAG, String.format("onSwipe() shift: %s", position)); - feedItemPos = (feedItemPos + position) % feedItems.length; - if (feedItemPos < 0) { - feedItemPos = feedItems.length - 1; - } - load(); - return true; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if(!isAdded() || item == null) { - return; - } - super.onCreateOptionsMenu(menu, inflater); - if (Flavors.FLAVOR == Flavors.PLAY) { - ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS); - } - inflater.inflate(R.menu.feeditem_options, menu); - popupMenu = menu; - if (item.hasMedia()) { - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item); - } else { - // these are already available via button1 and button2 - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, - R.id.mark_read_item, R.id.visit_website_item); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - switch(menuItem.getItemId()) { - case R.id.open_podcast: - openPodcast(); - return true; - default: - return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item); - } - } - - private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() { - @Override - public void setItemVisibility(int id, boolean visible) { - MenuItem item = popupMenu.findItem(id); - if (item != null) { - item.setVisible(visible); - } - } - }; - - private void onFragmentLoaded() { if (webviewData != null) { webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank"); @@ -374,7 +271,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { Log.d(TAG, "updateAppearance item is null"); return; } - getActivity().supportInvalidateOptionsMenu(); txtvPodcast.setText(item.getFeed().getTitle()); txtvTitle.setText(item.getTitle()); @@ -384,7 +280,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } Glide.with(getActivity()) - .load(item.getImageLocation()) + .load(ImageResourceUtils.getImageLocation(item)) .apply(new RequestOptions() .placeholder(R.color.light_gray) .error(R.color.light_gray) @@ -405,53 +301,59 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } FeedMedia media = item.getMedia(); - String butAction1Icon = null; - int butAction1Text = 0; - String butAction2Icon = null; - int butAction2Text = 0; + @AttrRes int butAction1Icon = 0; + @StringRes int butAction1Text = 0; + @AttrRes int butAction2Icon = 0; + @StringRes int butAction2Text = 0; if (media == null) { if (!item.isPlayed()) { - butAction1Icon = "{fa-check 24sp}"; + butAction1Icon = R.attr.navigation_accept; butAction1Text = R.string.mark_read_label; } if (item.getLink() != null) { - butAction2Icon = "{md-web 24sp}"; + butAction2Icon = R.attr.location_web_site; butAction2Text = R.string.visit_website_label; } } else { - if(media.getDuration() > 0) { + if (media.getDuration() > 0) { txtvDuration.setText(Converter.getDurationStringLong(media.getDuration())); } boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); if (!media.isDownloaded()) { - butAction2Icon = "{md-settings-input-antenna 24sp}"; + butAction2Icon = R.attr.action_stream; butAction2Text = R.string.stream_label; } else { - butAction2Icon = "{md-delete 24sp}"; + butAction2Icon = R.attr.content_discard; butAction2Text = R.string.delete_label; } if (isDownloading) { - butAction1Icon = "{md-cancel 24sp}"; + butAction1Icon = R.attr.navigation_cancel; butAction1Text = R.string.cancel_label; } else if (media.isDownloaded()) { - butAction1Icon = "{md-play-arrow 24sp}"; + butAction1Icon = R.attr.av_play; butAction1Text = R.string.play_label; } else { - butAction1Icon = "{md-file-download 24sp}"; + butAction1Icon = R.attr.av_download; butAction1Text = R.string.download_label; } } - if(butAction1Icon != null && butAction1Text != 0) { - butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text)); - Iconify.addIcons(butAction1); + if (butAction1Icon != 0 && butAction1Text != 0) { + butAction1.setText(butAction1Text); + butAction1.setTransformationMethod(null); + TypedValue typedValue = new TypedValue(); + getContext().getTheme().resolveAttribute(butAction1Icon, typedValue, true); + butAction1.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0); butAction1.setVisibility(View.VISIBLE); } else { butAction1.setVisibility(View.INVISIBLE); } - if(butAction2Icon != null && butAction2Text != 0) { - butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text)); - Iconify.addIcons(butAction2); + if (butAction2Icon != 0 && butAction2Text != 0) { + butAction2.setText(butAction2Text); + butAction2.setTransformationMethod(null); + TypedValue typedValue = new TypedValue(); + getContext().getTheme().resolveAttribute(butAction2Icon, typedValue, true); + butAction2.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0); butAction2.setVisibility(View.VISIBLE); } else { butAction2.setVisibility(View.INVISIBLE); @@ -533,8 +435,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); - for(FeedItem item : event.items) { - if(feedItems[feedItemPos] == item.getId()) { + for (FeedItem item : event.items) { + if (this.item.getId() == item.getId()) { load(); return; } @@ -557,18 +459,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } } - - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - load(); - } - } - }; + @Subscribe(threadMode = ThreadMode.MAIN) + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + load(); + } private void load() { - if(disposable != null) { + if (disposable != null) { disposable.dispose(); } progbarLoading.setVisibility(View.VISIBLE); @@ -585,7 +482,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Nullable private FeedItem loadInBackground() { - FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]); + FeedItem feedItem = DBReader.getFeedItem(itemId); Context context = getContext(); if (feedItem != null && context != null) { Timeline t = new Timeline(context, feedItem); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java new file mode 100644 index 000000000..fdac649d1 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -0,0 +1,196 @@ +package de.danoeh.antennapod.fragment; + +import android.os.Bundle; +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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.CastEnabledActivity; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.Flavors; +import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +/** + * Displays information about a list of FeedItems. + */ +public class ItemPagerFragment extends Fragment { + private static final String ARG_FEEDITEMS = "feeditems"; + private static final String ARG_FEEDITEM_POS = "feeditem_pos"; + + /** + * Creates a new instance of an ItemPagerFragment. + * + * @param feeditem The ID of the FeedItem that should be displayed. + * @return The ItemFragment instance + */ + public static ItemPagerFragment newInstance(long feeditem) { + return newInstance(new long[] { feeditem }, 0); + } + + /** + * Creates a new instance of an ItemPagerFragment. + * + * @param feeditems The IDs of the FeedItems that belong to the same list + * @param feedItemPos The position of the FeedItem that is currently shown + * @return The ItemFragment instance + */ + public static ItemPagerFragment newInstance(long[] feeditems, int feedItemPos) { + if (feeditems.length <= feedItemPos) { + throw new IllegalArgumentException("Trying to show a feed item that is out of the list"); + } + ItemPagerFragment fragment = new ItemPagerFragment(); + Bundle args = new Bundle(); + args.putLongArray(ARG_FEEDITEMS, feeditems); + args.putInt(ARG_FEEDITEM_POS, feedItemPos); + fragment.setArguments(args); + return fragment; + } + + private long[] feedItems; + private int feedItemPos; + private FeedItem item; + private Disposable disposable; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + + feedItems = getArguments().getLongArray(ARG_FEEDITEMS); + feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View layout = inflater.inflate(R.layout.feeditem_pager_fragment, container, false); + + ViewPager pager = layout.findViewById(R.id.pager); + // FragmentStatePagerAdapter documentation: + // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set. + // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc, + // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages. + int newId = ViewCompat.generateViewId(); + pager.setId(newId); + pager.setAdapter(new ItemPagerAdapter()); + pager.setCurrentItem(feedItemPos); + loadItem(feedItems[feedItemPos]); + pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + loadItem(feedItems[position]); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + return layout; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (disposable != null) { + disposable.dispose(); + } + } + + private void loadItem(long itemId) { + if (disposable != null) { + disposable.dispose(); + } + + disposable = Observable.fromCallable(() -> DBReader.getFeedItem(itemId)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + item = result; + getActivity().invalidateOptionsMenu(); + }, Throwable::printStackTrace); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (!isAdded() || item == null) { + return; + } + super.onCreateOptionsMenu(menu, inflater); + if (Flavors.FLAVOR == Flavors.PLAY) { + ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS); + } + inflater.inflate(R.menu.feeditem_options, menu); + + FeedItemMenuHandler.MenuInterface popupMenuInterface = (id, visible) -> { + MenuItem item = menu.findItem(id); + if (item != null) { + item.setVisible(visible); + } + }; + + if (item.hasMedia()) { + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item); + } else { + // these are already available via button1 and button2 + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, + R.id.mark_read_item, R.id.visit_website_item); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + switch (menuItem.getItemId()) { + case R.id.open_podcast: + openPodcast(); + return true; + default: + return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item); + } + } + + private void openPodcast() { + Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId()); + ((MainActivity) getActivity()).loadChildFragment(fragment); + } + + private class ItemPagerAdapter extends FragmentStatePagerAdapter { + + ItemPagerAdapter() { + super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NonNull + @Override + public Fragment getItem(int position) { + return ItemFragment.newInstance(feedItems[position]); + } + + @Override + public int getCount() { + return feedItems.length; + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java index 673b58901..4b1544e47 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.core.view.MenuItemCompat; import androidx.appcompat.widget.SearchView; @@ -18,8 +19,6 @@ import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; -import com.afollestad.materialdialogs.MaterialDialog; - import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; @@ -120,9 +119,9 @@ public class ItunesSearchFragment extends Fragment { progressBar.setVisibility(View.GONE); gridView.setVisibility(View.VISIBLE); String prefix = getString(R.string.error_msg_prefix); - new MaterialDialog.Builder(getActivity()) - .content(prefix + " " + error.getMessage()) - .neutralText(android.R.string.ok) + new AlertDialog.Builder(getActivity()) + .setMessage(prefix + " " + error.getMessage()) + .setPositiveButton(android.R.string.ok, null) .show(); }); }); @@ -151,7 +150,6 @@ public class ItunesSearchFragment extends Fragment { inflater.inflate(R.menu.itunes_search, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_itunes_label)); sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index 5dbb84bc7..2bfdd040b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -54,7 +54,8 @@ public class NewEpisodesFragment extends EpisodesListFragment { emptyView.setTitle(R.string.no_new_episodes_head_label); emptyView.setMessage(R.string.no_new_episodes_label); - ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { + ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, + ItemTouchHelper.RIGHT) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index f9fca87fc..a97e3dae8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -12,6 +12,8 @@ import android.view.MenuItem; import android.view.View; import android.widget.ListView; +import de.danoeh.antennapod.core.event.PlaybackHistoryEvent; +import de.danoeh.antennapod.core.event.PlayerStatusEvent; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -24,7 +26,6 @@ import de.danoeh.antennapod.adapter.FeedItemlistAdapter; 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.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.Downloader; @@ -39,12 +40,8 @@ import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; public class PlaybackHistoryFragment extends ListFragment { - public static final String TAG = "PlaybackHistoryFragment"; - private static final int EVENTS = EventDistributor.PLAYBACK_HISTORY_UPDATE | - EventDistributor.PLAYER_STATUS_UPDATE; - private List<FeedItem> playbackHistory; private FeedItemlistAdapter adapter; private List<Downloader> downloaderList; @@ -83,7 +80,6 @@ public class PlaybackHistoryFragment extends ListFragment { @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadItems(); } @@ -92,7 +88,6 @@ public class PlaybackHistoryFragment extends ListFragment { public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); - EventDistributor.getInstance().unregister(contentUpdate); if (disposable != null) { disposable.dispose(); } @@ -111,7 +106,7 @@ public class PlaybackHistoryFragment extends ListFragment { super.onListItemClick(l, v, position, id); position -= l.getHeaderViewsCount(); long[] ids = FeedItemUtil.getIds(playbackHistory); - ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position)); + ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position)); } @Override @@ -166,16 +161,17 @@ public class PlaybackHistoryFragment extends ListFragment { } } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Subscribe(threadMode = ThreadMode.MAIN) + public void onHistoryUpdated(PlaybackHistoryEvent event) { + loadItems(); + getActivity().supportInvalidateOptionsMenu(); + } - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - loadItems(); - getActivity().supportInvalidateOptionsMenu(); - } - } - }; + @Subscribe(threadMode = ThreadMode.MAIN) + public void onPlayerStatusChanged(PlayerStatusEvent event) { + loadItems(); + getActivity().supportInvalidateOptionsMenu(); + } private void onFragmentLoaded() { adapter.notifyDataSetChanged(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index b550669f3..b36ce6145 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -4,15 +4,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; -import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; -import androidx.core.view.MenuItemCompat; -import androidx.appcompat.app.AlertDialog; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.SearchView; -import androidx.recyclerview.widget.SimpleItemAnimator; -import androidx.recyclerview.widget.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -24,9 +15,18 @@ import android.widget.CheckBox; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SearchView; +import androidx.core.view.MenuItemCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.SimpleItemAnimator; + +import com.google.android.material.snackbar.Snackbar; 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; @@ -40,11 +40,13 @@ 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.event.FeedItemEvent; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import de.danoeh.antennapod.core.event.PlayerStatusEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.event.QueueEvent; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; -import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper; +import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; @@ -54,7 +56,6 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; 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; @@ -73,13 +74,8 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM * Shows all items in the queue */ public class QueueFragment extends Fragment { - public static final String TAG = "QueueFragment"; - private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | - EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset - EventDistributor.PLAYER_STATUS_UPDATE; - private TextView infoBar; private RecyclerView recyclerView; private QueueRecyclerAdapter recyclerAdapter; @@ -117,7 +113,6 @@ public class QueueFragment extends Fragment { onFragmentLoaded(true); } loadItems(true); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); } @@ -130,9 +125,8 @@ public class QueueFragment extends Fragment { @Override public void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - if(disposable != null) { + if (disposable != null) { disposable.dispose(); } } @@ -218,7 +212,31 @@ public class QueueFragment extends Fragment { @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(PlaybackPositionEvent event) { if (recyclerAdapter != null) { - recyclerAdapter.notifyCurrentlyPlayingItemChanged(event); + for (int i = 0; i < recyclerAdapter.getItemCount(); i++) { + QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder) + recyclerView.findViewHolderForAdapterPosition(i); + if (holder != null && holder.isCurrentlyPlayingItem()) { + holder.notifyPlaybackPositionUpdated(event); + break; + } + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onPlayerStatusChanged(PlayerStatusEvent event) { + loadItems(false); + if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { + getActivity().supportInvalidateOptionsMenu(); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + // Sent when playback position is reset + loadItems(false); + if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { + getActivity().supportInvalidateOptionsMenu(); } } @@ -270,7 +288,6 @@ public class QueueFragment extends Fragment { MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override @@ -331,7 +348,7 @@ public class QueueFragment extends Fragment { conDialog.createNewDialog().show(); return true; case R.id.episode_actions: - ((MainActivity) requireActivity()) .loadChildFragment( + ((MainActivity) requireActivity()).loadChildFragment( EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE)); return true; case R.id.queue_sort_episode_title_asc: @@ -373,7 +390,7 @@ public class QueueFragment extends Fragment { UserPreferences.setQueueKeepSorted(keepSortedNew); if (keepSortedNew) { SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder(); - QueueSorter.sort(sortOrder, true); + DBWriter.reorderQueue(sortOrder, true); if (recyclerAdapter != null) { recyclerAdapter.setLocked(true); } @@ -439,7 +456,7 @@ public class QueueFragment extends Fragment { */ private void setSortOrder(SortOrder sortOrder) { UserPreferences.setQueueKeepSortedOrder(sortOrder); - QueueSorter.sort(sortOrder, true); + DBWriter.reorderQueue(sortOrder, true); } @Override @@ -492,7 +509,8 @@ public class QueueFragment extends Fragment { registerForContextMenu(recyclerView); itemTouchHelper = new ItemTouchHelper( - new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) { + new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, + ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { // Position tracking whilst dragging int dragFrom = -1; @@ -634,14 +652,16 @@ public class QueueFragment extends Fragment { private void refreshInfoBar() { String info = queue.size() + getString(R.string.episodes_suffix); - if(queue.size() > 0) { + if (queue.size() > 0) { long timeLeft = 0; - for(FeedItem item : queue) { - float playbackSpeed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(item.getMedia()); - if(item.getMedia() != null) { - timeLeft += - (long) ((item.getMedia().getDuration() - item.getMedia().getPosition()) - / playbackSpeed); + for (FeedItem item : queue) { + float playbackSpeed = 1; + if (UserPreferences.timeRespectsSpeed()) { + playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia()); + } + if (item.getMedia() != null) { + long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition(); + timeLeft += itemTimeLeft / playbackSpeed; } } info += " • "; @@ -711,19 +731,6 @@ public class QueueFragment extends Fragment { } }; - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - Log.d(TAG, "arg: " + arg); - loadItems(false); - if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { - getActivity().supportInvalidateOptionsMenu(); - } - } - } - }; - private void loadItems(final boolean restoreScrollPosition) { Log.d(TAG, "loadItems()"); if(disposable != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index 226209740..7e217cde4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -11,7 +12,6 @@ import android.widget.AdapterView; import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; -import com.afollestad.materialdialogs.MaterialDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; @@ -110,9 +110,9 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. Log.e(TAG, Log.getStackTraceString(error)); view.setAlpha(1f); String prefix = getString(R.string.error_msg_prefix); - new MaterialDialog.Builder(getActivity()) - .content(prefix + " " + error.getMessage()) - .neutralText(android.R.string.ok) + new AlertDialog.Builder(getActivity()) + .setMessage(prefix + " " + error.getMessage()) + .setPositiveButton(android.R.string.ok, null) .show(); }); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java index 528fa7c32..7e8823c27 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -25,6 +25,7 @@ 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.view.EmptyViewHandler; +import org.greenrobot.eventbus.ThreadMode; /** * Displays all running downloads and provides actions to cancel them @@ -75,7 +76,7 @@ public class RunningDownloadsFragment extends ListFragment { setListAdapter(null); } - @Subscribe(sticky = true) + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(DownloadEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 7f3c60e5d..d124d6aa2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -20,7 +20,7 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.SearchlistAdapter; -import de.danoeh.antennapod.core.feed.EventDistributor; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; @@ -30,6 +30,8 @@ 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; /** * Performs a search operation on all feeds or one specific feed and displays the search result. @@ -76,7 +78,6 @@ public class SearchFragment extends ListFragment { @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); search(); } @@ -86,7 +87,6 @@ public class SearchFragment extends ListFragment { if(disposable != null) { disposable.dispose(); } - EventDistributor.getInstance().unregister(contentUpdate); } @Override @@ -103,6 +103,13 @@ public class SearchFragment extends ListFragment { searchAdapter = new SearchlistAdapter(getActivity(), itemAccess); setListAdapter(searchAdapter); + EventBus.getDefault().register(this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + EventBus.getDefault().unregister(this); } @Override @@ -145,15 +152,10 @@ public class SearchFragment extends ListFragment { MenuItemCompat.setActionView(item, sv); } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE - | EventDistributor.DOWNLOAD_HANDLED)) != 0) { - search(); - } - } - }; + @Subscribe + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + search(); + } private void onSearchResults(List<SearchResult> results) { searchResults = results; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 253c99c4e..af3649ed0 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -28,7 +28,8 @@ 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.feed.EventDistributor; +import de.danoeh.antennapod.core.event.FeedListUpdateEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; @@ -41,6 +42,7 @@ 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 de.danoeh.antennapod.view.EmptyViewHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -55,9 +57,6 @@ import org.greenrobot.eventbus.ThreadMode; public class SubscriptionFragment extends Fragment { public static final String TAG = "SubscriptionFragment"; - - private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE - | EventDistributor.UNREAD_ITEMS_UPDATE; private static final String PREFS = "SubscriptionFragment"; private static final String PREF_NUM_COLUMNS = "columns"; @@ -65,6 +64,7 @@ public class SubscriptionFragment extends Fragment { private DBReader.NavDrawerData navDrawerData; private SubscriptionsAdapter subscriptionAdapter; private FloatingActionButton subscriptionAddButton; + private EmptyViewHandler emptyView; private int mPosition = -1; private boolean isUpdatingFeeds = false; @@ -138,12 +138,22 @@ public class SubscriptionFragment extends Fragment { getActivity().invalidateOptionsMenu(); } + private void setupEmptyView() { + emptyView = new EmptyViewHandler(getContext()); + emptyView.setIcon(R.attr.ic_folder); + emptyView.setTitle(R.string.no_subscriptions_head_label); + emptyView.setMessage(R.string.no_subscriptions_label); + emptyView.attachToListView(subscriptionGridLayout); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + subscriptionAdapter = new SubscriptionsAdapter((MainActivity) getActivity(), itemAccess); subscriptionGridLayout.setAdapter(subscriptionAdapter); subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter); + setupEmptyView(); subscriptionAddButton.setOnClickListener(view -> { if (getActivity() instanceof MainActivity) { @@ -159,7 +169,6 @@ public class SubscriptionFragment extends Fragment { @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadSubscriptions(); } @@ -167,7 +176,6 @@ public class SubscriptionFragment extends Fragment { @Override public void onStop() { super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); if(disposable != null) { disposable.dispose(); @@ -178,12 +186,14 @@ public class SubscriptionFragment extends Fragment { if(disposable != null) { disposable.dispose(); } + emptyView.hide(); disposable = Observable.fromCallable(DBReader::getNavDrawerData) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { navDrawerData = result; subscriptionAdapter.notifyDataSetChanged(); + emptyView.updateVisibility(); }, error -> Log.e(TAG, Log.getStackTraceString(error))); } @@ -294,15 +304,15 @@ public class SubscriptionFragment extends Fragment { dialog.createNewDialog().show(); } - private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EVENTS & arg) != 0) { - Log.d(TAG, "Received contentUpdate Intent."); - loadSubscriptions(); - } - } - }; + @Subscribe + public void onFeedListChanged(FeedListUpdateEvent event) { + loadSubscriptions(); + } + + @Subscribe + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + loadSubscriptions(); + } @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 1d6debbfe..4baa74df6 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -54,7 +54,6 @@ public abstract class PodcastListFragment extends Fragment { inflater.inflate(R.menu.gpodder_podcasts, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java index 60fc1f446..ffe69aa9a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java @@ -50,7 +50,6 @@ public class SearchListFragment extends PodcastListFragment { // parent already inflated menu MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); sv.setQuery(query, false); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java index b5a95bc33..cde8fb3df 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java @@ -40,7 +40,6 @@ public class TagListFragment extends ListFragment { inflater.inflate(R.menu.gpodder_podcasts, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java new file mode 100644 index 000000000..62a5eb306 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java @@ -0,0 +1,65 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.ListFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.adapter.SimpleIconListAdapter; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; + +public class AboutDevelopersFragment extends ListFragment { + private Disposable developersLoader; + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setDivider(null); + getListView().setSelector(android.R.color.transparent); + + developersLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> { + ArrayList<SimpleIconListAdapter.ListItem> developers = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + getContext().getAssets().open("developers.csv"))); + String line; + while ((line = reader.readLine()) != null) { + String[] info = line.split(";"); + developers.add(new SimpleIconListAdapter.ListItem(info[0], info[2], + "https://avatars2.githubusercontent.com/u/" + info[1] + "?s=60&v=4")); + } + emitter.onSuccess(developers); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + developers -> setListAdapter(new SimpleIconListAdapter<>(getContext(), developers)), + error -> Toast.makeText(getContext(), "Error while loading developers", Toast.LENGTH_LONG).show() + ); + + } + + @Override + public void onStop() { + super.onStop(); + if (developersLoader != null) { + developersLoader.dispose(); + } + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.developers); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java new file mode 100644 index 000000000..0fa7bd4bb --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java @@ -0,0 +1,56 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Bundle; +import androidx.preference.PreferenceFragmentCompat; +import com.google.android.material.snackbar.Snackbar; +import de.danoeh.antennapod.BuildConfig; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.core.util.IntentUtils; + +public class AboutFragment extends PreferenceFragmentCompat { + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_about); + + findPreference("about_version").setSummary(String.format( + "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH)); + findPreference("about_version").setOnPreferenceClickListener((preference) -> { + ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), + findPreference("about_version").getSummary()); + clipboard.setPrimaryClip(clip); + Snackbar.make(getView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show(); + return true; + }); + findPreference("about_developers").setOnPreferenceClickListener((preference) -> { + getFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment()) + .addToBackStack(getString(R.string.developers)).commit(); + return true; + }); + findPreference("about_translators").setOnPreferenceClickListener((preference) -> { + getFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment()) + .addToBackStack(getString(R.string.translators)).commit(); + return true; + }); + findPreference("about_privacy_policy").setOnPreferenceClickListener((preference) -> { + IntentUtils.openInBrowser(getContext(), "https://antennapod.org/privacy.html"); + return true; + }); + findPreference("about_licenses").setOnPreferenceClickListener((preference) -> { + getFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment()) + .addToBackStack(getString(R.string.translators)).commit(); + return true; + }); + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.about_pref); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java new file mode 100644 index 000000000..536d11e01 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java @@ -0,0 +1,126 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.os.Bundle; +import android.view.View; +import android.widget.ListView; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.ListFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.adapter.SimpleIconListAdapter; +import de.danoeh.antennapod.core.util.IntentUtils; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; + +public class AboutLicensesFragment extends ListFragment { + private Disposable licensesLoader; + private final ArrayList<LicenseItem> licenses = new ArrayList<>(); + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setDivider(null); + + licensesLoader = Single.create((SingleOnSubscribe<ArrayList<LicenseItem>>) emitter -> { + licenses.clear(); + InputStream stream = getContext().getAssets().open("licenses.xml"); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + NodeList libraryList = docBuilder.parse(stream).getElementsByTagName("library"); + for (int i = 0; i < libraryList.getLength(); i++) { + NamedNodeMap lib = libraryList.item(i).getAttributes(); + licenses.add(new LicenseItem( + lib.getNamedItem("name").getTextContent(), + String.format("By %s, %s license", + lib.getNamedItem("author").getTextContent(), + lib.getNamedItem("license").getTextContent()), + null, + lib.getNamedItem("website").getTextContent(), + lib.getNamedItem("licenseText").getTextContent())); + } + emitter.onSuccess(licenses); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + developers -> setListAdapter(new SimpleIconListAdapter<LicenseItem>(getContext(), developers)), + error -> Toast.makeText(getContext(), "Error while loading licenses", Toast.LENGTH_LONG).show() + ); + + } + + private static class LicenseItem extends SimpleIconListAdapter.ListItem { + final String licenseUrl; + final String licenseTextFile; + + LicenseItem(String title, String subtitle, String imageUrl, String licenseUrl, String licenseTextFile) { + super(title, subtitle, imageUrl); + this.licenseUrl = licenseUrl; + this.licenseTextFile = licenseTextFile; + } + } + + @Override + public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) { + super.onListItemClick(l, v, position, id); + + LicenseItem item = licenses.get(position); + CharSequence[] items = {"View website", "View license"}; + new AlertDialog.Builder(getContext()) + .setTitle(item.title) + .setItems(items, (dialog, which) -> { + if (which == 0) { + IntentUtils.openInBrowser(getContext(), item.licenseUrl); + } else if (which == 1) { + showLicenseText(item.licenseTextFile); + } + }).show(); + } + + private void showLicenseText(String licenseTextFile) { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader( + getContext().getAssets().open(licenseTextFile))); + StringBuilder licenseText = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + licenseText.append(line).append("\n"); + } + + new AlertDialog.Builder(getContext()) + .setMessage(licenseText) + .show(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onStop() { + super.onStop(); + if (licensesLoader != null) { + licensesLoader.dispose(); + } + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.licenses); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java new file mode 100644 index 000000000..914dbb9a2 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java @@ -0,0 +1,64 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.ListFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.adapter.SimpleIconListAdapter; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; + +public class AboutTranslatorsFragment extends ListFragment { + private Disposable translatorsLoader; + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setDivider(null); + getListView().setSelector(android.R.color.transparent); + + translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> { + ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + getContext().getAssets().open("translators.csv"))); + String line; + while ((line = reader.readLine()) != null) { + String[] info = line.split(";"); + translators.add(new SimpleIconListAdapter.ListItem(info[0], info[1], null)); + } + emitter.onSuccess(translators); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)), + error -> Toast.makeText(getContext(), "Error while loading translators", Toast.LENGTH_LONG).show() + ); + + } + + @Override + public void onStop() { + super.onStop(); + if (translatorsLoader != null) { + translatorsLoader.dispose(); + } + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.translators); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java index 121b7fef8..fa17fed0a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java @@ -50,7 +50,7 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { @Override public void onStart() { super.onStart(); - ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_automatic_download_title); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java index 00e69f1db..5fd38d663 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java @@ -7,7 +7,6 @@ import androidx.preference.PreferenceFragmentCompat; 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.core.util.IntentUtils; @@ -63,7 +62,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { findPreference(PREF_ABOUT).setOnPreferenceClickListener( preference -> { - startActivity(new Intent(getActivity(), AboutActivity.class)); + getFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment()) + .addToBackStack(getString(R.string.about_pref)).commit(); return true; } ); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java index 440660942..1ca8f63aa 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java @@ -5,11 +5,8 @@ import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import android.text.format.DateFormat; -import com.afollestad.materialdialogs.MaterialDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -65,7 +62,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat { // validate and set correct value: number of downloads between 1 and 50 (inclusive) findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> { ProxyDialog dialog = new ProxyDialog(getActivity()); - dialog.createDialog().show(); + dialog.show(); return true; }); } @@ -107,13 +104,10 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat { private void showUpdateIntervalTimePreferencesDialog() { final Context context = getActivity(); - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder.title(R.string.pref_autoUpdateIntervallOrTime_title); - builder.content(R.string.pref_autoUpdateIntervallOrTime_message); - builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval); - builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay); - builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable); - builder.onPositive((dialog, which) -> { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title); + builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message); + builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_Interval, (dialog, which) -> { AlertDialog.Builder builder1 = new AlertDialog.Builder(context); builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval)); final String[] values = context.getResources().getStringArray(R.array.update_intervall_values); @@ -133,8 +127,9 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat { builder1.setNegativeButton(context.getString(R.string.cancel_label), null); builder1.show(); }); - builder.onNegative((dialog, which) -> { - int hourOfDay = 7, minute = 0; + builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, (dialog, which) -> { + int hourOfDay = 7; + int minute = 0; int[] updateTime = UserPreferences.getUpdateTimeOfDay(); if (updateTime.length == 2) { hourOfDay = updateTime[0]; @@ -151,7 +146,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat { timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay)); timePickerDialog.show(); }); - builder.onNeutral((dialog, which) -> { + builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Disable, (dialog, which) -> { UserPreferences.disableAutoUpdate(); setUpdateIntervalText(); }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index 1795dfc29..b82bba89b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -4,8 +4,15 @@ import android.app.Activity; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.collection.ArrayMap; import androidx.preference.ListPreference; +import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; + +import java.util.Map; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MediaplayerActivity; import de.danoeh.antennapod.activity.PreferenceActivity; @@ -34,12 +41,6 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref); } - @Override - public void onResume() { - super.onResume(); - checkSonicItemVisibility(); - } - private void setupPlaybackScreen() { final Activity activity = getActivity(); @@ -63,6 +64,43 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { behaviour.setEntries(R.array.video_background_behavior_options_without_pip); behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip); } + + buildEnqueueLocationPreference(); + } + + private void buildEnqueueLocationPreference() { + final Resources res = requireActivity().getResources(); + final Map<String, String> options = new ArrayMap<>(); + { + String[] keys = res.getStringArray(R.array.enqueue_location_values); + String[] values = res.getStringArray(R.array.enqueue_location_options); + for (int i = 0; i < keys.length; i++) { + options.put(keys[i], values[i]); + } + } + + ListPreference pref = requirePreference(UserPreferences.PREF_ENQUEUE_LOCATION); + pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(pref.getValue()))); + + pref.setOnPreferenceChangeListener((preference, newValue) -> { + if (!(newValue instanceof String)) { + return false; + } + String newValStr = (String)newValue; + pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(newValStr))); + return true; + }); + } + + @NonNull + private <T extends Preference> T requirePreference(@NonNull CharSequence key) { + // Possibly put it to a common method in abstract base class + T result = findPreference(key); + if (result == null) { + throw new IllegalArgumentException("Preference with key '" + key + "' is not found"); + + } + return result; } private void buildSmartMarkAsPlayedPreference() { @@ -86,14 +124,4 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { } pref.setEntries(entries); } - - - - private void checkSonicItemVisibility() { - if (Build.VERSION.SDK_INT < 16) { - ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER); - p.setEntries(R.array.media_player_options_no_sonic); - p.setEntryValues(R.array.media_player_values_no_sonic); - } - } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index 60eaf14a5..2f3459beb 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -13,6 +13,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBWriter; @@ -200,6 +201,10 @@ public class FeedItemMenuHandler { break; case R.id.reset_position: selectedItem.getMedia().setPosition(0); + if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == selectedItem.getMedia().getId()) { + PlaybackPreferences.writeNoMediaPlaying(); + IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE); + } DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, true); break; case R.id.activate_auto_download: diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index 5d012168e..e32deba27 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -2,12 +2,15 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; import android.content.DialogInterface; -import androidx.appcompat.app.AlertDialog; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import androidx.annotation.NonNull; + +import org.apache.commons.lang3.StringUtils; + import java.util.Set; import de.danoeh.antennapod.R; @@ -18,7 +21,9 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; +import de.danoeh.antennapod.core.util.SortOrder; import de.danoeh.antennapod.dialog.FilterDialog; +import de.danoeh.antennapod.dialog.IntraFeedSortDialog; /** * Handles interactions with the FeedItemMenu. @@ -42,6 +47,10 @@ public class FeedMenuHandler { Log.d(TAG, "Preparing options menu"); menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged()); + if (StringUtils.isBlank(selectedFeed.getLink())) { + menu.findItem(R.id.visit_website_item).setVisible(false); + menu.findItem(R.id.share_link_item).setVisible(false); + } return true; } @@ -60,6 +69,9 @@ public class FeedMenuHandler { case R.id.refresh_complete_item: DBTasks.forceRefreshCompleteFeed(context, selectedFeed); break; + case R.id.sort_items: + showSortDialog(context, selectedFeed); + break; case R.id.filter_items: showFilterDialog(context, selectedFeed); break; @@ -103,4 +115,17 @@ public class FeedMenuHandler { filterDialog.openDialog(); } + + + private static void showSortDialog(Context context, Feed selectedFeed) { + IntraFeedSortDialog sortDialog = new IntraFeedSortDialog(context, selectedFeed.getSortOrder()) { + @Override + protected void updateSort(@NonNull SortOrder sortOrder) { + selectedFeed.setSortOrder(sortOrder); + DBWriter.setFeedItemSortOrder(selectedFeed.getId(), sortOrder); + } + }; + sortDialog.openDialog(); + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java index 4a57726b1..64eb72ee3 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java @@ -2,14 +2,8 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Color; -import android.os.Build; -import androidx.appcompat.widget.SearchView; import android.view.Menu; import android.view.MenuItem; -import android.widget.EditText; - -import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; /** @@ -17,18 +11,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; */ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuItemUtils { - public static void adjustTextColor(Context context, SearchView sv) { - if(Build.VERSION.SDK_INT < 14) { - EditText searchEditText = sv.findViewById(R.id.search_src_text); - if (UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark - || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) { - searchEditText.setTextColor(Color.WHITE); - } else { - searchEditText.setTextColor(Color.BLACK); - } - } - } - @SuppressWarnings("ResourceType") public static void refreshLockItem(Context context, Menu menu) { final MenuItem queueLock = menu.findItem(de.danoeh.antennapod.R.id.queue_lock); diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java index 6392d0535..4686f0303 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java @@ -7,6 +7,7 @@ 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.preferences.UserPreferences.EnqueueLocation; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.core.util.gui.NotificationUtils; @@ -23,7 +24,6 @@ public class PreferenceUpgrader { int newVersion = BuildConfig.VERSION_CODE; if (oldVersion != newVersion) { - NotificationUtils.createChannels(context); AutoUpdateManager.restartUpdateAlarm(); upgrade(oldVersion); @@ -32,6 +32,9 @@ public class PreferenceUpgrader { } private static void upgrade(int oldVersion) { + if (oldVersion == -1) { + return; + } if (oldVersion < 1070196) { // migrate episode cleanup value (unit changed from days to hours) int oldValueInDays = UserPreferences.getEpisodeCleanupValue(); @@ -72,6 +75,14 @@ public class PreferenceUpgrader { } UserPreferences.setQueueLocked(false); + prefs.edit().putBoolean(UserPreferences.PREF_STREAM_OVER_DOWNLOAD, false).apply(); + + if (!prefs.contains(UserPreferences.PREF_ENQUEUE_LOCATION)) { + final String keyOldPrefEnqueueFront = "prefQueueAddToFront"; + boolean enqueueAtFront = prefs.getBoolean(keyOldPrefEnqueueFront, false); + EnqueueLocation enqueueLocation = enqueueAtFront ? EnqueueLocation.FRONT : EnqueueLocation.BACK; + UserPreferences.setEnqueueLocation(enqueueLocation); + } } } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java index e79389fb3..4c116fa2d 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java @@ -88,7 +88,7 @@ public class AspectRatioVideoView extends VideoView { mVideoWidth = videoWidth; mVideoHeight = videoHeight; - /** + /* * If this isn't set the video is stretched across the * SurfaceHolders display surface (i.e. the SurfaceHolder * as the same size and the video is drawn to fit this diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java index 1516c4eb6..a2d8ec091 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java @@ -1,7 +1,10 @@ package de.danoeh.antennapod.view;
import android.content.Context;
+import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
import androidx.annotation.AttrRes;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -9,7 +12,6 @@ import android.util.TypedValue; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -17,8 +19,9 @@ import de.danoeh.antennapod.R; public class EmptyViewHandler {
private boolean layoutAdded = false;
- private RecyclerView recyclerView;
- private RecyclerView.Adapter adapter;
+ private View list;
+ private ListAdapter listAdapter;
+ private RecyclerView.Adapter recyclerAdapter;
private final Context context;
private final View emptyView;
@@ -54,44 +57,60 @@ public class EmptyViewHandler { emptyView.setVisibility(View.GONE);
}
- public void attachToListView(ListView listView) {
+ public void attachToListView(AbsListView listView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(listView);
layoutAdded = true;
- ((ViewGroup) listView.getParent()).addView(emptyView);
+ this.list = listView;
listView.setEmptyView(emptyView);
+ updateAdapter(listView.getAdapter());
}
public void attachToRecyclerView(RecyclerView recyclerView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(recyclerView);
layoutAdded = true;
- this.recyclerView = recyclerView;
- ViewGroup parent = ((ViewGroup) recyclerView.getParent());
- parent.addView(emptyView);
+ this.list = recyclerView;
updateAdapter(recyclerView.getAdapter());
+ }
+ private void addToParentView(View view) {
+ ViewGroup parent = ((ViewGroup) view.getParent());
+ parent.addView(emptyView);
if (parent instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ (RelativeLayout.LayoutParams) emptyView.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
emptyView.setLayoutParams(layoutParams);
}
}
public void updateAdapter(RecyclerView.Adapter adapter) {
- if (this.adapter != null) {
- this.adapter.unregisterAdapterDataObserver(adapterObserver);
+ if (this.recyclerAdapter != null) {
+ this.recyclerAdapter.unregisterAdapterDataObserver(adapterObserver);
}
- this.adapter = adapter;
+ this.recyclerAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(adapterObserver);
}
updateVisibility();
}
+ private void updateAdapter(ListAdapter adapter) {
+ if (this.listAdapter != null) {
+ this.listAdapter.unregisterDataSetObserver(listAdapterObserver);
+ }
+ this.listAdapter = adapter;
+ if (adapter != null) {
+ adapter.registerDataSetObserver(listAdapterObserver);
+ }
+ updateVisibility();
+ }
+
private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
@Override
public void anythingChanged() {
@@ -99,14 +118,22 @@ public class EmptyViewHandler { }
};
- private void updateVisibility() {
+ private final DataSetObserver listAdapterObserver = new DataSetObserver() {
+ public void onChanged() {
+ updateVisibility();
+ }
+ };
+
+ public void updateVisibility() {
boolean empty;
- if (adapter == null) {
- empty = true;
+ if (recyclerAdapter != null) {
+ empty = recyclerAdapter.getItemCount() == 0;
+ } else if (listAdapter != null) {
+ empty = listAdapter.isEmpty();
} else {
- empty = adapter.getItemCount() == 0;
+ empty = true;
}
- recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
+ list.setVisibility(empty ? View.GONE : View.VISIBLE);
emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
}
}
|