diff options
Diffstat (limited to 'app/src/main')
45 files changed, 906 insertions, 670 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 216afc6b7..919abeb03 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,6 +32,7 @@ android:label="@string/app_name" android:backupAgent=".core.backup.OpmlBackupAgent" android:restoreAnyVersion="true" + android:usesCleartextTraffic="true" android:logo="@mipmap/ic_launcher"> <meta-data android:name="com.google.android.gms.car.notification.SmallIcon" android:resource="@drawable/ic_notification" /> diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index 5dd4a357b..1bcdada44 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -1,7 +1,9 @@ package de.danoeh.antennapod.activity; +import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -54,12 +56,15 @@ public class AboutActivity extends AppCompatActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (!url.startsWith("http")) { + if (url.startsWith("http")) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(browserIntent); + return true; + } else { url = url.replace("file:///android_asset/", ""); loadAsset(url); return true; } - return false; } }); 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 67dda01cf..2bcd7a461 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -129,6 +129,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { } UserPreferences.setPlaybackSpeed(newSpeed); controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); + onPositionObserverUpdate(); } else { VariableSpeedDialog.showGetPluginDialog(this); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java index e6c9c37cc..9795c1240 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java @@ -23,6 +23,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; +import java.util.Arrays; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -109,9 +110,14 @@ public class ImportExportActivity extends AppCompatActivity { } private void restoreFrom(Uri inputUri) { - File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME); InputStream inputStream = null; try { + if (!validateDB(inputUri)) { + displayBadFileDialog(); + return; + } + + File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME); inputStream = getContentResolver().openInputStream(inputUri); FileUtils.copyInputStreamToFile(inputStream, currentDB); displayImportSuccessDialog(); @@ -123,6 +129,28 @@ public class ImportExportActivity extends AppCompatActivity { } } + private static final byte[] SQLITE3_MAGIC = "SQLite format 3\0".getBytes(); + private boolean validateDB(Uri inputUri) throws IOException { + try (InputStream inputStream = getContentResolver().openInputStream(inputUri)) { + byte[] magicBuf = new byte[SQLITE3_MAGIC.length]; + if (inputStream.read(magicBuf) == magicBuf.length) { + return Arrays.equals(SQLITE3_MAGIC, magicBuf); + } + } + + return false; + } + + private void displayBadFileDialog() { + AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this); + d.setMessage(R.string.import_bad_file) + .setCancelable(false) + .setPositiveButton(android.R.string.ok, ((dialogInterface, i) -> { + // do nothing + })) + .show(); + } + private void displayImportSuccessDialog() { AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this); d.setMessage(R.string.import_ok); 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 e670fa9ed..ee8455a8c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -43,7 +43,6 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.event.QueueEvent; -import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; @@ -68,11 +67,13 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.menuhandler.NavDrawerActivity; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * The activity that is shown when the user launches the app. @@ -763,6 +764,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi }, error -> Log.e(TAG, Log.getStackTraceString(error))); } + @Subscribe public void onEvent(QueueEvent event) { Log.d(TAG, "onEvent(" + event + ")"); // we are only interested in the number of queue items, not download status or position @@ -774,6 +776,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi loadData(); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(ProgressEvent event) { Log.d(TAG, "onEvent(" + event + ")"); switch(event.action) { @@ -792,6 +795,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(MessageEvent event) { Log.d(TAG, "onEvent(" + event + ")"); View parentLayout = findViewById(R.id.drawer_layout); 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 86d4ec642..3d5c59a4a 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -38,7 +38,6 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons; import java.util.Locale; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; @@ -56,6 +55,7 @@ import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.Supplier; +import de.danoeh.antennapod.core.util.TimeSpeedConverter; import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.MediaPlayerError; @@ -661,15 +661,6 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements StorageUtils.checkStorageAvailability(this); } - public void onEventMainThread(ServiceEvent event) { - Log.d(TAG, "onEvent(" + event + ")"); - if (event.action == ServiceEvent.Action.SERVICE_STARTED) { - if (controller != null) { - controller.init(); - } - } - } - /** * Called by 'handleStatus()' when the PlaybackService is waiting for * a video surface. @@ -684,8 +675,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements if (controller == null || txtvPosition == null || txtvLength == null) { return; } - int currentPosition = controller.getPosition(); - int duration = controller.getDuration(); + + int currentPosition = TimeSpeedConverter.convert(controller.getPosition()); + int duration = TimeSpeedConverter.convert(controller.getDuration()); + int remainingTime = TimeSpeedConverter.convert( + controller.getDuration() - controller.getPosition()); Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition)); if (currentPosition == PlaybackService.INVALID_TIME || duration == PlaybackService.INVALID_TIME) { @@ -694,7 +688,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } txtvPosition.setText(Converter.getDurationStringLong(currentPosition)); if (showTimeLeft) { - txtvLength.setText("-" + Converter.getDurationStringLong(duration - currentPosition)); + txtvLength.setText("-" + Converter.getDurationStringLong(remainingTime)); } else { txtvLength.setText(Converter.getDurationStringLong(duration)); } @@ -842,9 +836,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements String length; if (showTimeLeft) { - length = "-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition()); + int remainingTime = TimeSpeedConverter.convert( + media.getDuration() - media.getPosition()); + + length = "-" + Converter.getDurationStringLong(remainingTime); } else { - length = Converter.getDurationStringLong(media.getDuration()); + int duration = TimeSpeedConverter.convert(media.getDuration()); + length = Converter.getDurationStringLong(duration); } txtvLength.setText(length); @@ -947,7 +945,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition); if (showTimeLeft && prog != 0) { int duration = controller.getDuration(); - String length = "-" + Converter.getDurationStringLong(duration - (int) (prog * duration)); + int timeLeft = TimeSpeedConverter.convert(duration - (int) (prog * duration)); + String length = "-" + Converter.getDurationStringLong(timeLeft); txtvLength.setText(length); } } 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 99e579712..191481dd8 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -7,7 +7,6 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; @@ -34,7 +33,6 @@ import com.viewpagerindicator.CirclePageIndicator; import java.util.List; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.ChaptersListAdapter; import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; @@ -62,11 +60,13 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.menuhandler.NavDrawerActivity; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Activity for playing files that do not require a video surface. @@ -108,12 +108,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem private Disposable disposable; @Override - protected void onPause() { - super.onPause(); - EventBus.getDefault().unregister(this); - } - - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportPostponeEnterTransition(); @@ -127,6 +121,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem disposable.dispose(); } EventDistributor.getInstance().unregister(contentUpdate); + EventBus.getDefault().unregister(this); saveCurrentFragment(); } @@ -175,10 +170,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem pager.setCurrentItem(lastPosition); } - @Override - protected void onResume() { - super.onResume(); - + protected void onStart() { + super.onStart(); EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadData(); @@ -442,6 +435,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem }, error -> Log.e(TAG, Log.getStackTraceString(error))); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(MessageEvent event) { Log.d(TAG, "onEvent(" + event + ")"); View parentLayout = findViewById(R.id.drawer_layout); 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 145908e97..35c423a47 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -31,6 +31,9 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; 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; @@ -68,7 +71,6 @@ import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.dialog.AuthenticationDialog; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -123,6 +125,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } }; + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); setSubscribeButtonState(feed); @@ -193,24 +196,19 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } @Override - protected void onResume() { - super.onResume(); + protected void onStart() { + super.onStart(); isPaused = false; EventDistributor.getInstance().register(listener); EventBus.getDefault().register(this); } @Override - protected void onPause() { - super.onPause(); + protected void onStop() { + super.onStop(); isPaused = true; EventDistributor.getInstance().unregister(listener); EventBus.getDefault().unregister(this); - } - - @Override - protected void onStop() { - super.onStop(); if (downloader != null && !downloader.isFinished()) { downloader.cancel(); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java index a63d3b735..158e3b7a4 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java @@ -25,7 +25,6 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { private static final int CHOOSE_OPML_FILE = 1; - private Intent intentPickAction; private Intent intentGetContentAction; @Override @@ -36,50 +35,30 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setContentView(R.layout.opml_import); - final TextView txtvHeaderExplanation1 = findViewById(R.id.txtvHeadingExplanation1); - final TextView txtvExplanation1 = findViewById(R.id.txtvExplanation1); - final TextView txtvHeaderExplanation2 = findViewById(R.id.txtvHeadingExplanation2); - final TextView txtvExplanation2 = findViewById(R.id.txtvExplanation2); - final TextView txtvHeaderExplanation3 = findViewById(R.id.txtvHeadingExplanation3); + final TextView txtvHeaderExplanation = findViewById(R.id.txtvHeadingExplanation); + final TextView txtvExplanation = findViewById(R.id.txtvExplanation); + final TextView txtvHeaderExplanationOpenWith = findViewById(R.id.txtvHeadingExplanationOpenWith); Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem); - butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem()); - - Button butChooseExternal = findViewById(R.id.butChooseFileFromExternal); - butChooseExternal.setOnClickListener(v -> chooseFileFromExternal()); + butChooseFilesystem.setOnClickListener(v -> chooseFileFromExternal()); int nextOption = 1; String optionLabel = getString(R.string.opml_import_option); - intentPickAction = new Intent(Intent.ACTION_PICK); - - if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { - intentPickAction.setData(null); - if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { - txtvHeaderExplanation1.setVisibility(View.GONE); - txtvExplanation1.setVisibility(View.GONE); - findViewById(R.id.divider1).setVisibility(View.GONE); - butChooseFilesystem.setVisibility(View.GONE); - } - } - if(txtvExplanation1.getVisibility() == View.VISIBLE) { - txtvHeaderExplanation1.setText(String.format(optionLabel, nextOption)); - nextOption++; - } - intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT); intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE); intentGetContentAction.setType("*/*"); - if(!IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) { - txtvHeaderExplanation2.setVisibility(View.GONE); - txtvExplanation2.setVisibility(View.GONE); - findViewById(R.id.divider2).setVisibility(View.GONE); - butChooseExternal.setVisibility(View.GONE); - } else { - txtvHeaderExplanation2.setText(String.format(optionLabel, nextOption)); + + if (IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) { + txtvHeaderExplanation.setText(String.format(optionLabel, nextOption)); nextOption++; + } else { + txtvHeaderExplanation.setVisibility(View.GONE); + txtvExplanation.setVisibility(View.GONE); + findViewById(R.id.divider).setVisibility(View.GONE); + butChooseFilesystem.setVisibility(View.GONE); } - txtvHeaderExplanation3.setText(String.format(optionLabel, nextOption)); + txtvHeaderExplanationOpenWith.setText(String.format(optionLabel, nextOption)); } @Override @@ -106,18 +85,6 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { } } - /* - * Creates an implicit intent to launch a file manager which lets - * the user choose a specific OPML-file to import from. - */ - private void chooseFileFromFilesystem() { - try { - startActivityForResult(intentPickAction, CHOOSE_OPML_FILE); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "No activity found. Should never happen..."); - } - } - private void chooseFileFromExternal() { try { startActivityForResult(intentGetContentAction, CHOOSE_OPML_FILE); 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 fa5012b74..78cc15b2c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -47,7 +47,6 @@ public class VideoplayerActivity extends MediaplayerActivity { */ private boolean videoControlsShowing = true; private boolean videoSurfaceCreated = false; - private boolean playbackStoppedUponExitVideo = false; private boolean destroyingDueToReload = false; private VideoControlsHider videoControlsHider = new VideoControlsHider(this); @@ -78,7 +77,6 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onResume() { super.onResume(); - playbackStoppedUponExitVideo = false; if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { playExternalMedia(getIntent(), MediaType.VIDEO); } else if (PlaybackService.isCasting()) { @@ -93,32 +91,12 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onStop() { - stopPlaybackIfUserPreferencesSpecified(); // MUST be called before super.onStop(), while it still has member variable controller super.onStop(); if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { videoControlsHider.stop(); } } - void stopPlaybackIfUserPreferencesSpecified() { - // to avoid the method being called twice during leaving Videoplayer - // , which will double-pause the media - // (it is usually first called by surfaceHolderCallback.surfaceDestroyed(), - // then VideoplayerActivity.onStop() , but sometimes VideoplayerActivity.onStop() - // will first be invoked.) - if (playbackStoppedUponExitVideo) { - return; - } - playbackStoppedUponExitVideo = true; - - if (controller != null && !destroyingDueToReload - && UserPreferences.getVideoBackgroundBehavior() - != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { - Log.v(TAG, "stop video playback per UserPreference"); - controller.notifyVideoSurfaceAbandoned(); - } - } - @Override public void onUserLeaveHint () { if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior() @@ -297,12 +275,13 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override public void surfaceDestroyed(SurfaceHolder holder) { - Log.d(TAG, "Videosurface was destroyed." ); - Log.v(TAG, " hasController=" + (controller != null) - + " , destroyingDueToReload=" + destroyingDueToReload - + " , videoBackgroundBehavior=" + UserPreferences.getVideoBackgroundBehavior()); + Log.d(TAG, "Videosurface was destroyed"); videoSurfaceCreated = false; - stopPlaybackIfUserPreferencesSpecified(); + if (controller != null && !destroyingDueToReload + && UserPreferences.getVideoBackgroundBehavior() + != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { + controller.notifyVideoSurfaceAbandoned(); + } } }; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java deleted file mode 100644 index e6b42efcb..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.util.LongList; - -interface ActionButtonCallback { - /** Is called when the action button of a list item has been pressed. */ - void onActionButtonPressed(FeedItem item, LongList queueIds); -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java deleted file mode 100644 index a915692d1..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.content.res.TypedArray; -import android.view.View; -import android.widget.ImageButton; - -import org.apache.commons.lang3.Validate; - -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.DownloadRequester; - -/** - * Utility methods for the action button that is displayed on the right hand side - * of a listitem. - */ -class ActionButtonUtils { - - private final int[] labels; - private final TypedArray drawables; - private final Context context; - - public ActionButtonUtils(Context context) { - Validate.notNull(context); - - this.context = context.getApplicationContext(); - drawables = context.obtainStyledAttributes(new int[] { - R.attr.av_play, - R.attr.navigation_cancel, - R.attr.av_download, - R.attr.av_pause, - R.attr.navigation_accept, - R.attr.content_new - }); - labels = new int[] { - R.string.play_label, - R.string.cancel_download_label, - R.string.download_label, - R.string.mark_read_label, - R.string.add_to_queue_label - }; - } - - /** - * Sets the displayed bitmap and content description of the given - * action button so that it matches the state of the FeedItem. - */ - @SuppressWarnings("ResourceType") - public void configureActionButton(ImageButton butSecondary, FeedItem item, boolean isInQueue) { - Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null"); - - final FeedMedia media = item.getMedia(); - if (media != null) { - final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); - if (!media.isDownloaded()) { - if (isDownloadingMedia) { - // item is being downloaded - butSecondary.setVisibility(View.VISIBLE); - butSecondary.setImageDrawable(drawables.getDrawable(1)); - butSecondary.setContentDescription(context.getString(labels[1])); - } else { - // item is not downloaded and not being downloaded - if(DefaultActionButtonCallback.userAllowedMobileDownloads() || - !DefaultActionButtonCallback.userChoseAddToQueue() || isInQueue) { - butSecondary.setVisibility(View.VISIBLE); - butSecondary.setImageDrawable(drawables.getDrawable(2)); - butSecondary.setContentDescription(context.getString(labels[2])); - } else { - // mobile download not allowed yet, item is not in queue and user chose add to queue - butSecondary.setVisibility(View.VISIBLE); - butSecondary.setImageDrawable(drawables.getDrawable(5)); - butSecondary.setContentDescription(context.getString(labels[4])); - } - } - } else { - // item is downloaded - butSecondary.setVisibility(View.VISIBLE); - if (media.isCurrentlyPlaying()) { - butSecondary.setImageDrawable(drawables.getDrawable(3)); - } else { - butSecondary.setImageDrawable(drawables.getDrawable(0)); - } - butSecondary.setContentDescription(context.getString(labels[0])); - } - } else { - if (item.isPlayed()) { - butSecondary.setVisibility(View.INVISIBLE); - } else { - butSecondary.setVisibility(View.VISIBLE); - butSecondary.setImageDrawable(drawables.getDrawable(4)); - butSecondary.setContentDescription(context.getString(labels[3])); - } - } - } -} 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 0b2b81edb..8866d987e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.adapter; +import android.content.Context; import android.os.Build; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; @@ -26,6 +27,7 @@ import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -46,8 +48,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR private final WeakReference<MainActivity> mainActivityRef; private final ItemAccess itemAccess; - private final ActionButtonCallback actionButtonCallback; - private final ActionButtonUtils actionButtonUtils; private final boolean showOnlyNewEpisodes; private FeedItem selectedItem; @@ -57,13 +57,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR public AllEpisodesRecycleAdapter(MainActivity mainActivity, ItemAccess itemAccess, - ActionButtonCallback actionButtonCallback, boolean showOnlyNewEpisodes) { super(); this.mainActivityRef = new WeakReference<>(mainActivity); this.itemAccess = itemAccess; - this.actionButtonUtils = new ActionButtonUtils(mainActivity); - this.actionButtonCallback = actionButtonCallback; this.showOnlyNewEpisodes = showOnlyNewEpisodes; playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background); @@ -186,10 +183,11 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.queueStatus.setVisibility(View.INVISIBLE); } - actionButtonUtils.configureActionButton(holder.butSecondary, item, isInQueue); + ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue); + actionButton.configure(holder.butSecondary, mainActivityRef.get()); + holder.butSecondary.setFocusable(false); holder.butSecondary.setTag(item); - holder.butSecondary.setOnClickListener(secondaryActionListener); new CoverLoader(mainActivityRef.get()) .withUri(item.getImageLocation()) @@ -215,14 +213,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR return itemAccess.getCount(); } - private final View.OnClickListener secondaryActionListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - FeedItem item = (FeedItem) v.getTag(); - actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds()); - } - }; - public class Holder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnCreateContextMenuListener, diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java new file mode 100644 index 000000000..636a23088 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java @@ -0,0 +1,147 @@ +package de.danoeh.antennapod.adapter; + +import android.app.Dialog; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RadioButton; +import android.widget.TextView; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import de.danoeh.antennapod.R; +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> { + private final ChooseDataFolderDialog.RunnableWithString selectionHandler; + private final String currentPath; + private final List<StoragePath> entries; + private final String freeSpaceString; + private Dialog dialog; + + public DataFolderAdapter(Context context, ChooseDataFolderDialog.RunnableWithString selectionHandler) { + this.entries = getStorageEntries(context); + this.currentPath = getCurrentPath(); + this.selectionHandler = selectionHandler; + this.freeSpaceString = context.getString(R.string.choose_data_directory_available_space); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View entryView = inflater.inflate(R.layout.choose_data_folder_dialog_entry, parent, false); + return new ViewHolder(entryView); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + StoragePath storagePath = entries.get(position); + String freeSpace = Converter.byteToString(storagePath.getAvailableSpace()); + + holder.path.setText(storagePath.getShortPath()); + holder.size.setText(String.format(freeSpaceString, freeSpace)); + holder.progressBar.setProgress(storagePath.getUsagePercentage()); + holder.root.setOnClickListener((View v) -> selectAndDismiss(storagePath)); + holder.radioButton.setOnClickListener((View v) -> selectAndDismiss(storagePath)); + if (storagePath.getFullPath().equals(currentPath)) { + holder.radioButton.toggle(); + } + } + + @Override + public int getItemCount() { + if (currentPath == null) { + return 0; + } else { + return entries.size(); + } + } + + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } + + private String getCurrentPath() { + File dataFolder = UserPreferences.getDataFolder(null); + if (dataFolder != null) return dataFolder.getAbsolutePath(); + return null; + } + + private List<StoragePath> getStorageEntries(Context context) { + File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null); + final List<StoragePath> entries = new ArrayList<>(mediaDirs.length); + for (File dir : mediaDirs) { + if (isNotWritable(dir)) continue; + + entries.add(new StoragePath(dir.getAbsolutePath())); + } + return entries; + } + + private boolean isNotWritable(File dir) { + return dir == null || !dir.exists() || !dir.canRead() || !dir.canWrite(); + } + + private void selectAndDismiss(StoragePath storagePath) { + selectionHandler.run(storagePath.getFullPath()); + dialog.dismiss(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + private View root; + private TextView path; + private TextView size; + private RadioButton radioButton; + private MaterialProgressBar progressBar; + + ViewHolder(View itemView) { + super(itemView); + root = itemView.findViewById(R.id.root); + path = itemView.findViewById(R.id.path); + size = itemView.findViewById(R.id.size); + radioButton = itemView.findViewById(R.id.radio_button); + progressBar = itemView.findViewById(R.id.used_space); + } + } + + class StoragePath { + private final String path; + + StoragePath(String path) { + this.path = path; + } + + String getShortPath() { + int prefixIndex = path.indexOf("Android"); + return (prefixIndex > 0) ? path.substring(0, prefixIndex) : path; + } + + String getFullPath() { + return this.path; + } + + long getAvailableSpace() { + return StorageUtils.getFreeSpaceAvailable(path); + } + + long getTotalSpace() { + return StorageUtils.getTotalSpaceAvailable(path); + } + + int getUsagePercentage() { + return 100 - (int) (100 * getAvailableSpace() / (float) getTotalSpace()); + } + } +}
\ No newline at end of file diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java deleted file mode 100644 index f54b9266e..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java +++ /dev/null @@ -1,133 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.widget.Toast; - -import com.afollestad.materialdialogs.MaterialDialog; - -import org.apache.commons.lang3.Validate; - -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.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBTasks; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.storage.DownloadRequestException; -import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.core.util.NetworkUtils; -import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; - -/** - * Default implementation of an ActionButtonCallback - */ -public class DefaultActionButtonCallback implements ActionButtonCallback { - - private static final String TAG = "DefaultActionBtnCb"; - - private final Context context; - - private static final int TEN_MINUTES_IN_MILLIS = 60 * 1000 * 10; - - // remember timestamp when user allowed downloading via mobile connection - private static long allowMobileDownloadsTimestamp; - private static long onlyAddToQueueTimeStamp; - - public DefaultActionButtonCallback(Context context) { - Validate.notNull(context); - this.context = context; - } - - public static boolean userAllowedMobileDownloads() { - return System.currentTimeMillis() - allowMobileDownloadsTimestamp < TEN_MINUTES_IN_MILLIS; - } - - public static boolean userChoseAddToQueue() { - return System.currentTimeMillis() - onlyAddToQueueTimeStamp < TEN_MINUTES_IN_MILLIS; - } - - @Override - public void onActionButtonPressed(final FeedItem item, final LongList queueIds) { - - if (item.hasMedia()) { - final FeedMedia media = item.getMedia(); - boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); - if (!isDownloading && !media.isDownloaded()) { - if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) { - try { - DBTasks.downloadFeedItems(context, item); - Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage()); - } - } else if(userChoseAddToQueue() && !queueIds.contains(item.getId())) { - DBWriter.addQueueItem(context, item); - Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show(); - } else { - confirmMobileDownload(context, item); - } - } else if (isDownloading) { - DownloadRequester.getInstance().cancelDownload(context, media); - if(UserPreferences.isEnableAutodownload()) { - DBWriter.setFeedItemAutoDownload(media.getItem(), false); - Toast.makeText(context, R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show(); - } - } else { // media is downloaded - if (media.isCurrentlyPlaying()) { - IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); - } else if (media.isCurrentlyPaused()) { - new PlaybackServiceStarter(context, media) // need to start the service in case it's been stopped by system. - .startWhenPrepared(true) - .shouldStream(false) - .start(); - IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE); - } else { - DBTasks.playMedia(context, media, false, true, false); - } - } - } else { - if (!item.isPlayed()) { - DBWriter.markItemPlayed(item, FeedItem.PLAYED, true); - } - } - } - - private void confirmMobileDownload(final Context context, final FeedItem item) { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder - .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) -> { - allowMobileDownloadsTimestamp = System.currentTimeMillis(); - try { - DBTasks.downloadFeedItems(context, item); - Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage()); - } - }); - LongList queueIds = DBReader.getQueueIDList(); - if(!queueIds.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) -> { - onlyAddToQueueTimeStamp = System.currentTimeMillis(); - DBWriter.addQueueItem(context, item); - Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show(); - }); - } - builder.show(); - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java index 738a0a636..a365b1b2e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java @@ -7,7 +7,6 @@ import android.support.v4.content.ContextCompat; import android.text.Layout; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Adapter; import android.widget.BaseAdapter; @@ -18,6 +17,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; @@ -31,14 +31,12 @@ import de.danoeh.antennapod.core.util.ThemeUtils; */ public class FeedItemlistAdapter extends BaseAdapter { - private final ActionButtonCallback callback; private final ItemAccess itemAccess; private final Context context; private final boolean showFeedtitle; private final int selectedItemIndex; /** true if played items should be made partially transparent */ private final boolean makePlayedItemsTransparent; - private final ActionButtonUtils actionButtonUtils; private static final int SELECTION_NONE = -1; @@ -47,16 +45,13 @@ public class FeedItemlistAdapter extends BaseAdapter { public FeedItemlistAdapter(Context context, ItemAccess itemAccess, - ActionButtonCallback callback, boolean showFeedtitle, boolean makePlayedItemsTransparent) { super(); - this.callback = callback; this.context = context; this.itemAccess = itemAccess; this.showFeedtitle = showFeedtitle; this.selectedItemIndex = SELECTION_NONE; - this.actionButtonUtils = new ActionButtonUtils(context); this.makePlayedItemsTransparent = makePlayedItemsTransparent; playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background); @@ -199,10 +194,11 @@ public class FeedItemlistAdapter extends BaseAdapter { } } - actionButtonUtils.configureActionButton(holder.butAction, item, isInQueue); + ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue); + actionButton.configure(holder.butAction, context); + holder.butAction.setFocusable(false); holder.butAction.setTag(item); - holder.butAction.setOnClickListener(butActionListener); } else { convertView.setVisibility(View.GONE); @@ -210,14 +206,6 @@ public class FeedItemlistAdapter extends BaseAdapter { return convertView; } - private final OnClickListener butActionListener = new OnClickListener() { - @Override - public void onClick(View v) { - FeedItem item = (FeedItem) v.getTag(); - callback.onActionButtonPressed(item, itemAccess.getQueueIds()); - } - }; - static class Holder { LinearLayout container; TextView title; 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 df8cafb9d..d9d54ac15 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.adapter; +import android.content.Context; import android.os.Build; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; @@ -22,8 +23,6 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import org.apache.commons.lang3.ArrayUtils; @@ -32,9 +31,9 @@ import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; -import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; @@ -54,8 +53,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap private final WeakReference<MainActivity> mainActivity; private final ItemAccess itemAccess; - private final ActionButtonCallback actionButtonCallback; - private final ActionButtonUtils actionButtonUtils; private final ItemTouchHelper itemTouchHelper; private boolean locked; @@ -67,13 +64,10 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap public QueueRecyclerAdapter(MainActivity mainActivity, ItemAccess itemAccess, - ActionButtonCallback actionButtonCallback, ItemTouchHelper itemTouchHelper) { super(); this.mainActivity = new WeakReference<>(mainActivity); this.itemAccess = itemAccess; - this.actionButtonUtils = new ActionButtonUtils(mainActivity); - this.actionButtonCallback = actionButtonCallback; this.itemTouchHelper = itemTouchHelper; locked = UserPreferences.isQueueLocked(); @@ -287,10 +281,11 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap } } - actionButtonUtils.configureActionButton(butSecondary, item, true); + ItemActionButton actionButton = ItemActionButton.forItem(item, true); + actionButton.configure(butSecondary, mainActivity.get()); + butSecondary.setFocusable(false); butSecondary.setTag(item); - butSecondary.setOnClickListener(secondaryActionListener); new CoverLoader(mainActivity.get()) .withUri(item.getImageLocation()) @@ -302,15 +297,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap } - private final View.OnClickListener secondaryActionListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - FeedItem item = (FeedItem) v.getTag(); - actionButtonCallback.onActionButtonPressed(item, itemAccess.getQueueIds()); - } - }; - - public interface ItemAccess { FeedItem getItem(int position); int getCount(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java new file mode 100644 index 000000000..3299db3ab --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java @@ -0,0 +1,32 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.StringRes; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; + +class AddToQueueActionButton extends ItemActionButton { + + AddToQueueActionButton(FeedItem item) { + super(item); + } + + @Override + @StringRes + public int getLabel() { + return R.string.add_to_queue_label; + } + + @Override + @AttrRes + public int getDrawable() { + return R.attr.content_new; + } + + @Override + public void onClick(Context context) { + MobileDownloadHelper.confirmMobileDownload(context, item); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java new file mode 100644 index 000000000..1275a799b --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java @@ -0,0 +1,44 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.StringRes; +import android.widget.Toast; + +import de.danoeh.antennapod.R; +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.DBWriter; +import de.danoeh.antennapod.core.storage.DownloadRequester; + +class CancelDownloadActionButton extends ItemActionButton { + + CancelDownloadActionButton(FeedItem item) { + super(item); + } + + @Override + @StringRes + public int getLabel() { + return R.string.cancel_download_label; + } + + @Override + @AttrRes + public int getDrawable() { + return R.attr.navigation_cancel; + } + + @Override + public void onClick(Context context) { + FeedMedia media = item.getMedia(); + DownloadRequester.getInstance().cancelDownload(context, media); + if (UserPreferences.isEnableAutodownload()) { + DBWriter.setFeedItemAutoDownload(media.getItem(), false); + Toast.makeText(context, R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show(); + } + } +} 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 new file mode 100644 index 000000000..202a41161 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java @@ -0,0 +1,74 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.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; +import de.danoeh.antennapod.core.util.NetworkUtils; + +class DownloadActionButton extends ItemActionButton { + private boolean isInQueue; + + DownloadActionButton(FeedItem item, boolean isInQueue) { + super(item); + this.isInQueue = isInQueue; + } + + @Override + @StringRes + public int getLabel() { + return R.string.download_label; + } + + @Override + @AttrRes + public int getDrawable() { + return R.attr.av_download; + } + + @Override + public void onClick(Context context) { + final FeedMedia media = item.getMedia(); + if (media == null || shouldNotDownload(media)) { + return; + } + + if (NetworkUtils.isDownloadAllowed() || MobileDownloadHelper.userAllowedMobileDownloads()) { + downloadEpisode(context); + } else if (MobileDownloadHelper.userChoseAddToQueue() && !isInQueue) { + addEpisodeToQueue(context); + } else { + MobileDownloadHelper.confirmMobileDownload(context, item); + } + } + + private boolean shouldNotDownload(@NonNull FeedMedia media) { + boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); + return isDownloading || media.isDownloaded(); + } + + private void addEpisodeToQueue(Context context) { + DBWriter.addQueueItem(context, item); + Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show(); + } + + private void downloadEpisode(Context context) { + try { + DBTasks.downloadFeedItems(context, item); + Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage()); + } + } +} 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 new file mode 100644 index 000000000..da5ebf6e1 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java @@ -0,0 +1,63 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.view.View; +import android.widget.ImageButton; + +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.storage.DownloadRequester; + +public abstract class ItemActionButton { + FeedItem item; + + ItemActionButton(FeedItem item) { + this.item = item; + } + + @StringRes + abstract public int getLabel(); + + @AttrRes + abstract public int getDrawable(); + + abstract public void onClick(Context context); + + public int getVisibility() { + return View.VISIBLE; + } + + @NonNull + public static ItemActionButton forItem(@NonNull FeedItem item, boolean isInQueue) { + final FeedMedia media = item.getMedia(); + if (media == null) { + return new MarkAsPlayedActionButton(item); + } + + final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); + if (media.isDownloaded()) { + return new PlayActionButton(item); + } else if (isDownloadingMedia) { + return new CancelDownloadActionButton(item); + } else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) { + return new DownloadActionButton(item, isInQueue); + } else { + return new AddToQueueActionButton(item); + } + } + + public void configure(@NonNull ImageButton button, Context context) { + TypedArray drawables = context.obtainStyledAttributes(new int[]{getDrawable()}); + + button.setVisibility(getVisibility()); + button.setContentDescription(context.getString(getLabel())); + button.setImageDrawable(drawables.getDrawable(0)); + button.setOnClickListener((view) -> onClick(context)); + + drawables.recycle(); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java new file mode 100644 index 000000000..4d906cee5 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java @@ -0,0 +1,41 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.annotation.StringRes; +import android.view.View; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.storage.DBWriter; + +class MarkAsPlayedActionButton extends ItemActionButton { + + MarkAsPlayedActionButton(FeedItem item) { + super(item); + } + + @Override + @StringRes + public int getLabel() { + return R.string.mark_read_label; + } + + @Override + @AttrRes + public int getDrawable() { + return R.attr.navigation_accept; + } + + @Override + public void onClick(Context context) { + if (!item.isPlayed()) { + DBWriter.markItemPlayed(item, FeedItem.PLAYED, true); + } + } + + @Override + public int getVisibility() { + return (item.isPlayed()) ? View.INVISIBLE : View.VISIBLE; + } +} 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 new file mode 100644 index 000000000..f8d2a139e --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java @@ -0,0 +1,60 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.widget.Toast; + +import com.afollestad.materialdialogs.MaterialDialog; + +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; + +class MobileDownloadHelper { + private static long addToQueueTimestamp; + private static long allowMobileDownloadTimestamp; + private static final int TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000; + + static boolean userChoseAddToQueue() { + return System.currentTimeMillis() - addToQueueTimestamp < TEN_MINUTES_IN_MILLIS; + } + + static boolean userAllowedMobileDownloads() { + return System.currentTimeMillis() - allowMobileDownloadTimestamp < TEN_MINUTES_IN_MILLIS; + } + + 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)); + 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.show(); + } + + private static void addToQueue(Context context, FeedItem item) { + addToQueueTimestamp = System.currentTimeMillis(); + DBWriter.addQueueItem(context, item); + Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show(); + } + + private static void downloadFeedItems(Context context, FeedItem item) { + allowMobileDownloadTimestamp = System.currentTimeMillis(); + try { + DBTasks.downloadFeedItems(context, item); + Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage()); + } + } +}
\ No newline at end of file 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 new file mode 100644 index 000000000..3992c7240 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java @@ -0,0 +1,63 @@ +package de.danoeh.antennapod.adapter.actionbutton; + +import android.content.Context; +import android.support.annotation.AttrRes; +import android.support.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; +import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE; + +class PlayActionButton extends ItemActionButton { + + PlayActionButton(FeedItem item) { + super(item); + } + + @Override + @StringRes + public int getLabel() { + return R.string.play_label; + } + + @Override + @AttrRes + public int getDrawable() { + FeedMedia media = item.getMedia(); + if (media != null && media.isCurrentlyPlaying()) { + return R.attr.av_pause; + } else { + return R.attr.av_play; + } + } + + @Override + public void onClick(Context context) { + FeedMedia media = item.getMedia(); + if (media == null) { + return; + } + + if (media.isPlaying()) { + togglePlayPause(context, media); + } else { + DBTasks.playMedia(context, media, false, true, false); + } + } + + 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); + } +} 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 e8faa7c29..c185a5557 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java @@ -1,20 +1,11 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import android.os.Build; -import android.support.v4.content.ContextCompat; -import android.text.Html; import com.afollestad.materialdialogs.MaterialDialog; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - import de.danoeh.antennapod.R; -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.adapter.DataFolderAdapter; public class ChooseDataFolderDialog { @@ -31,39 +22,9 @@ public class ChooseDataFolderDialog { private ChooseDataFolderDialog() {} public static void showDialog(final Context context, RunnableWithString handlerFunc) { - File dataFolder = UserPreferences.getDataFolder(null); - if (dataFolder == null) { - new MaterialDialog.Builder(context) - .title(R.string.error_label) - .content(R.string.external_storage_error_msg) - .neutralText(android.R.string.ok) - .show(); - return; - } - String dataFolderPath = dataFolder.getAbsolutePath(); - int selectedIndex = -1; - int index = 0; - File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null); - final List<String> folders = new ArrayList<>(mediaDirs.length); - final List<CharSequence> choices = new ArrayList<>(mediaDirs.length); - for (File dir : mediaDirs) { - if(dir == null || !dir.exists() || !dir.canRead() || !dir.canWrite()) { - continue; - } - String path = dir.getAbsolutePath(); - folders.add(path); - if(dataFolderPath.equals(path)) { - selectedIndex = index; - } - int prefixIndex = path.indexOf("Android"); - String choice = (prefixIndex > 0) ? path.substring(0, prefixIndex) : path; - long bytes = StorageUtils.getFreeSpaceAvailable(path); - String item = String.format( - "<small>%1$s [%2$s]</small>", choice, Converter.byteToString(bytes)); - choices.add(fromHtmlVersioned(item)); - index++; - } - if (choices.isEmpty()) { + 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) @@ -71,27 +32,16 @@ public class ChooseDataFolderDialog { .show(); return; } + MaterialDialog dialog = new MaterialDialog.Builder(context) .title(R.string.choose_data_directory) .content(R.string.choose_data_directory_message) - .items(choices) - .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> { - String folder = folders.get(which); - handlerFunc.run(folder); - return true; - }) + .adapter(adapter, null) .negativeText(R.string.cancel_label) .cancelable(true) .build(); + adapter.setDialog(dialog); dialog.show(); } - @SuppressWarnings("deprecation") - private static CharSequence fromHtmlVersioned(final String html) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - return Html.fromHtml(html); - } - return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); - } - }
\ No newline at end of file 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 ece184035..24656ed29 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.util.Log; import com.afollestad.materialdialogs.MaterialDialog; @@ -73,7 +74,8 @@ public class RatingDialog { return mPreferences.getBoolean(KEY_RATED, false); } - private static void saveRated() { + @VisibleForTesting + public static void saveRated() { mPreferences .edit() .putBoolean(KEY_RATED, true) 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 4b8601ec6..dc056a3f9 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java @@ -18,7 +18,7 @@ import com.afollestad.materialdialogs.MaterialDialog; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; -import de.greenrobot.event.EventBus; +import org.greenrobot.eventbus.EventBus; public abstract class SleepTimerDialog { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index f2ae47ac4..8f4e9f656 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -31,7 +31,6 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; @@ -51,12 +50,15 @@ import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; + import de.danoeh.antennapod.view.EmptyViewHandler; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Shows unread or recently published episodes @@ -107,12 +109,12 @@ public class AllEpisodesFragment extends Fragment { if (viewsCreated && itemsLoaded) { onFragmentLoaded(); } + EventBus.getDefault().register(this); } @Override public void onResume() { super.onResume(); - EventBus.getDefault().registerSticky(this); loadItems(); registerForContextMenu(recyclerView); } @@ -120,7 +122,6 @@ public class AllEpisodesFragment extends Fragment { @Override public void onPause() { super.onPause(); - EventBus.getDefault().unregister(this); saveScrollPosition(); unregisterForContextMenu(recyclerView); } @@ -128,6 +129,7 @@ public class AllEpisodesFragment extends Fragment { @Override public void onStop() { super.onStop(); + EventBus.getDefault().unregister(this); EventDistributor.getInstance().unregister(contentUpdate); if (disposable != null) { disposable.dispose(); @@ -347,8 +349,7 @@ public class AllEpisodesFragment extends Fragment { if (episodes != null && episodes.size() > 0) { if (listAdapter == null) { MainActivity mainActivity = (MainActivity) getActivity(); - listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, - new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes()); + listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes()); listAdapter.setHasStableIds(true); recyclerView.setAdapter(listAdapter); emptyView.updateAdapter(listAdapter); @@ -430,6 +431,7 @@ public class AllEpisodesFragment extends Fragment { }; + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if (episodes == null) { @@ -456,6 +458,7 @@ public class AllEpisodesFragment extends Fragment { return true; } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java index 8f2073ed0..0ffd1a8da 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -2,22 +2,29 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.util.Log; import android.view.View; import android.widget.ListView; +import java.util.List; +import java.util.ListIterator; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.ChaptersListAdapter; -import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; -import de.greenrobot.event.EventBus; +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; public class ChaptersFragment extends ListFragment { private static final String TAG = "ChaptersFragment"; private ChaptersListAdapter adapter; private PlaybackController controller; + private Disposable disposable; @Override @@ -42,10 +49,7 @@ public class ChaptersFragment extends ListFragment { controller = new PlaybackController(getActivity(), false) { @Override public boolean loadMediaInfo() { - if (getMedia() == null) { - return false; - } - onMediaChanged(getMedia()); + ChaptersFragment.this.loadMediaInfo(); return true; } @@ -55,22 +59,59 @@ public class ChaptersFragment extends ListFragment { } }; controller.init(); - onMediaChanged(controller.getMedia()); - EventBus.getDefault().register(this); + + loadMediaInfo(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (disposable != null) { + disposable.dispose(); + } } @Override public void onStop() { super.onStop(); - EventBus.getDefault().unregister(this); controller.release(); controller = null; } - public void onEventMainThread(ServiceEvent event) { - if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) { - controller.init(); + private void scrollTo(int position) { + getListView().setSelection(position); + } + + private int getCurrentChapter(Playable media) { + int currentPosition = controller.getPosition(); + + List<Chapter> chapters = media.getChapters(); + for (final ListIterator<Chapter> it = chapters.listIterator(); it.hasNext(); ) { + Chapter chapter = it.next(); + if (chapter.getStart() > currentPosition) { + return it.previousIndex() - 1; + } + } + return chapters.size() - 1; + } + + private void loadMediaInfo() { + if (disposable != null) { + disposable.dispose(); } + disposable = Maybe.create(emitter -> { + Playable media = controller.getMedia(); + if (media != null) { + emitter.onSuccess(media); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(media -> onMediaChanged((Playable) media), + error -> Log.e(TAG, Log.getStackTraceString(error))); } private void onMediaChanged(Playable media) { @@ -81,6 +122,7 @@ public class ChaptersFragment extends ListFragment { setEmptyText(getString(R.string.no_chapters_label)); } else { setEmptyText(null); + scrollTo(getCurrentChapter(media)); } } } 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 88d748c5a..7fd61d3ad 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -13,11 +14,13 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; -import de.greenrobot.event.EventBus; +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays the cover and the title of a FeedItem. @@ -31,6 +34,7 @@ public class CoverFragment extends Fragment { private TextView txtvEpisodeTitle; private ImageView imgvCover; private PlaybackController controller; + private Disposable disposable; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -44,11 +48,24 @@ public class CoverFragment extends Fragment { } private void loadMediaInfo() { - Playable media = controller.getMedia(); - if (media == null) { - Log.w(TAG, "loadMediaInfo was called while media was null"); - return; + if (disposable != null) { + disposable.dispose(); } + disposable = Maybe.create(emitter -> { + Playable media = controller.getMedia(); + if (media != null) { + emitter.onSuccess(media); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(media -> displayMediaInfo((Playable) media), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + + private void displayMediaInfo(@NonNull Playable media) { txtvPodcastTitle.setText(media.getFeedTitle()); txtvEpisodeTitle.setText(media.getEpisodeTitle()); Glide.with(this) @@ -73,9 +90,6 @@ public class CoverFragment extends Fragment { controller = new PlaybackController(getActivity(), false) { @Override public boolean loadMediaInfo() { - if (getMedia() == null) { - return false; - } CoverFragment.this.loadMediaInfo(); return true; } @@ -83,20 +97,21 @@ public class CoverFragment extends Fragment { }; controller.init(); loadMediaInfo(); - EventBus.getDefault().register(this); - } - - public void onEventMainThread(ServiceEvent event) { - if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) { - controller.init(); - } } @Override public void onStop() { super.onStop(); - EventBus.getDefault().unregister(this); controller.release(); controller = null; } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (disposable != null) { + disposable.dispose(); + } + } } 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 dca574984..348c73b92 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -24,7 +24,6 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; -import de.greenrobot.event.EventBus; import io.reactivex.Maybe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -92,13 +91,6 @@ public class ExternalPlayerFragment extends Fragment { loadMediaInfo(); } - public void onEventMainThread(ServiceEvent event) { - Log.d(TAG, "onEvent(" + event + ")"); - if (event.action == ServiceEvent.Action.SERVICE_STARTED) { - controller.init(); - } - } - private PlaybackController setupPlaybackController() { return new PlaybackController(getActivity(), true) { @@ -146,7 +138,6 @@ public class ExternalPlayerFragment extends Fragment { controller = setupPlaybackController(); controller.init(); loadMediaInfo(); - EventBus.getDefault().register(this); } @Override @@ -156,7 +147,6 @@ public class ExternalPlayerFragment extends Fragment { controller.release(); controller = null; } - EventBus.getDefault().unregister(this); } @Override 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 d4a662d43..d362d5c0b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -18,6 +18,8 @@ import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** @@ -37,6 +39,7 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { @Override protected String getPrefName() { return PREF_NAME; } + @Subscribe public void onEvent(FavoritesEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); loadItems(); 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 9cee59531..5cf2c5eeb 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -10,6 +10,7 @@ import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -28,7 +29,6 @@ import android.widget.Toast; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MediaplayerInfoActivity; -import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.IntentUtils; @@ -36,7 +36,6 @@ import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.Timeline; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -63,7 +62,6 @@ public class ItemDescriptionFragment extends Fragment { private String selectedURL; - @SuppressLint("NewApi") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -83,6 +81,10 @@ public class ItemDescriptionFragment extends Fragment { webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // Use cached resources, even if they have expired } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } + webvDescription.getSettings().setUseWideViewPort(false); webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); webvDescription.getSettings().setLoadWithOverviewMode(true); @@ -310,19 +312,11 @@ public class ItemDescriptionFragment extends Fragment { }; controller.init(); load(); - EventBus.getDefault().register(this); - } - - public void onEventMainThread(ServiceEvent event) { - if (event.action == ServiceEvent.Action.SERVICE_STARTED && controller != null) { - controller.init(); - } } @Override public void onStop() { super.onStop(); - EventBus.getDefault().unregister(this); controller.release(); controller = null; } 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 e114ef405..a2472b071 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -42,7 +42,7 @@ 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.DefaultActionButtonCallback; +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; @@ -61,18 +61,19 @@ import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.playback.Timeline; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.view.OnSwipeGesture; import de.danoeh.antennapod.view.SwipeGestureDetector; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Displays information about a FeedItem and actions. @@ -196,6 +197,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // Use cached resources, even if they have expired } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } webvDescription.getSettings().setUseWideViewPort(false); webvDescription.getSettings().setLayoutAlgorithm( WebSettings.LayoutAlgorithm.NARROW_COLUMNS); @@ -227,9 +231,9 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { if (item == null) { return; } - DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity()); - actionButtonCallback.onActionButtonPressed(item, item.isTagged(FeedItem.TAG_QUEUE) ? - LongList.of(item.getId()) : new LongList(0)); + ItemActionButton actionButton = ItemActionButton.forItem(item, item.isTagged(FeedItem.TAG_QUEUE)); + actionButton.onClick(getActivity()); + FeedMedia media = item.getMedia(); if (media != null && media.isDownloaded()) { // playback was started, dialog should close itself @@ -266,10 +270,15 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + EventBus.getDefault().register(this); + } + + @Override public void onResume() { super.onResume(); - EventDistributor.getInstance().register(contentUpdate); - EventBus.getDefault().registerSticky(this); if(itemsLoaded) { progbarLoading.setVisibility(View.GONE); updateAppearance(); @@ -277,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } @Override - public void onPause() { - super.onPause(); + public void onStop() { + super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); } @@ -537,6 +546,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { ((MainActivity)getActivity()).loadChildFragment(fragment); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); for(FeedItem item : event.items) { @@ -547,6 +557,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; @@ -592,7 +603,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { private FeedItem loadInBackground() { FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]); if (feedItem != null) { - Timeline t = new Timeline(getActivity(), feedItem); + Timeline t = new Timeline(getContext(), feedItem); webviewData = t.processShownotes(false); } return feedItem; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index 726de8369..6a04758b9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -37,7 +37,6 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.FeedInfoActivity; import de.danoeh.antennapod.activity.FeedSettingsActivity; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.FeedItemlistAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; @@ -68,11 +67,13 @@ import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Displays a list of FeedItems. @@ -143,6 +144,8 @@ public class ItemlistFragment extends ListFragment { @Override public void onStart() { super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + EventBus.getDefault().register(this); if (viewsCreated && itemsLoaded) { onFragmentLoaded(); } @@ -151,16 +154,14 @@ public class ItemlistFragment extends ListFragment { @Override public void onResume() { super.onResume(); - EventDistributor.getInstance().register(contentUpdate); - EventBus.getDefault().registerSticky(this); ((MainActivity)getActivity()).getSupportActionBar().setTitle(""); updateProgressBarVisibility(); loadItems(); } @Override - public void onPause() { - super.onPause(); + public void onStop() { + super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); if(disposable != null) { @@ -359,6 +360,7 @@ public class ItemlistFragment extends ListFragment { activity.getSupportActionBar().setTitle(feed.getTitle()); } + @Subscribe public void onEvent(FeedEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); if(event.feedId == feedID) { @@ -366,6 +368,7 @@ public class ItemlistFragment extends ListFragment { } } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if(feed == null || feed.getItems() == null || adapter == null) { @@ -380,6 +383,7 @@ public class ItemlistFragment extends ListFragment { } } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; @@ -423,7 +427,7 @@ public class ItemlistFragment extends ListFragment { setListAdapter(null); setupHeaderView(); setupFooterView(); - adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true); + adapter = new FeedItemlistAdapter(getActivity(), itemAccess, false, true); setListAdapter(adapter); } refreshHeaderView(); 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 ba4b55123..a9f56d317 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; +import android.support.annotation.NonNull; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -106,7 +107,7 @@ public class ItunesSearchFragment extends Fragment { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); @@ -146,7 +147,9 @@ public class ItunesSearchFragment extends Fragment { emitter.onError(new IOException(prefix + response)); } } catch (IOException | JSONException e) { - emitter.onError(e); + if (!disposable.isDisposed()) { + emitter.onError(e); + } } }) .subscribeOn(Schedulers.io()) @@ -249,7 +252,9 @@ public class ItunesSearchFragment extends Fragment { List<Podcast> podcasts = parseFeed(feedString); emitter.onSuccess(podcasts); } catch (IOException | JSONException e) { - emitter.onError(e); + if (!disposable.isDisposed()) { + emitter.onError(e); + } } }) .subscribeOn(Schedulers.io()) 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 e8f35b180..da11383a5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -17,7 +17,6 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.FeedItemlistAdapter; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; @@ -31,11 +30,13 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.view.EmptyViewHandler; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; public class PlaybackHistoryFragment extends ListFragment { @@ -91,29 +92,18 @@ public class PlaybackHistoryFragment extends ListFragment { } - - @Override - public void onResume() { - super.onResume(); - EventBus.getDefault().registerSticky(this); - loadItems(); - } - @Override public void onStart() { super.onStart(); EventDistributor.getInstance().register(contentUpdate); - } - - @Override - public void onPause() { - super.onPause(); - EventBus.getDefault().unregister(this); + EventBus.getDefault().register(this); + loadItems(); } @Override public void onStop() { super.onStop(); + EventBus.getDefault().unregister(this); EventDistributor.getInstance().unregister(contentUpdate); if(disposable != null) { disposable.dispose(); @@ -135,6 +125,7 @@ public class PlaybackHistoryFragment extends ListFragment { viewsCreated = false; } + @Subscribe(sticky = true) public void onEvent(DownloadEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; @@ -193,6 +184,7 @@ public class PlaybackHistoryFragment extends ListFragment { } } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if(playbackHistory == null) { @@ -223,8 +215,7 @@ public class PlaybackHistoryFragment extends ListFragment { // played items shoudln't be transparent for this fragment since, *all* items // in this fragment will, by definition, be played. So it serves no purpose and can make // it harder to read. - adapter = new FeedItemlistAdapter(getActivity(), itemAccess, - new DefaultActionButtonCallback(getActivity()), true, false); + adapter = new FeedItemlistAdapter(getActivity(), itemAccess, true, false); setListAdapter(adapter); } setListShown(true); 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 13580be4f..71b3c27a2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -28,7 +28,6 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.QueueRecyclerAdapter; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.DownloadEvent; @@ -52,12 +51,15 @@ import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; + import de.danoeh.antennapod.view.EmptyViewHandler; -import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Shows all items in the queue @@ -105,7 +107,7 @@ public class QueueFragment extends Fragment { } loadItems(true); EventDistributor.getInstance().register(contentUpdate); - EventBus.getDefault().registerSticky(this); + EventBus.getDefault().register(this); } @Override @@ -124,6 +126,7 @@ public class QueueFragment extends Fragment { } } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(QueueEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if (queue == null) { @@ -162,6 +165,7 @@ public class QueueFragment extends Fragment { onFragmentLoaded(false); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if (queue == null) { @@ -182,6 +186,7 @@ public class QueueFragment extends Fragment { } } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; @@ -519,8 +524,7 @@ public class QueueFragment extends Fragment { if (queue != null && queue.size() > 0) { if (recyclerAdapter == null) { MainActivity activity = (MainActivity) getActivity(); - recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess, - new DefaultActionButtonCallback(activity), itemTouchHelper); + recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess, itemTouchHelper); recyclerAdapter.setHasStableIds(true); recyclerView.setAdapter(recyclerAdapter); emptyView.updateAdapter(recyclerAdapter); 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 3c40b542c..718502ea2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -21,7 +21,8 @@ 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 de.greenrobot.event.EventBus; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; /** * Displays all running downloads and provides actions to cancel them @@ -54,14 +55,14 @@ public class RunningDownloadsFragment extends ListFragment { } @Override - public void onResume() { - super.onResume(); - EventBus.getDefault().registerSticky(this); + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); } @Override - public void onPause() { - super.onPause(); + public void onStop() { + super.onStop(); EventBus.getDefault().unregister(this); } @@ -72,6 +73,7 @@ public class RunningDownloadsFragment extends ListFragment { adapter = null; } + @Subscribe(sticky = true) public void onEvent(DownloadEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; diff --git a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml new file mode 100644 index 000000000..9a216b36b --- /dev/null +++ b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/root" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="@dimen/diag_content_side_padding" + android:background="?attr/selectableItemBackground"> + + <RadioButton + android:id="@+id/radio_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:padding="4dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:layout_toEndOf="@+id/radio_button" + android:layout_toRightOf="@+id/radio_button" + android:orientation="vertical"> + + <TextView + android:id="@+id/path" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="/storage/sdcard0" /> + + <TextView + android:id="@+id/size" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="2 GB" /> + + <me.zhanghai.android.materialprogressbar.MaterialProgressBar + android:id="@+id/used_space" + style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:mpb_progressStyle="horizontal" /> + </LinearLayout> + +</RelativeLayout> diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml index b1e93a195..fec7be26c 100644 --- a/app/src/main/res/layout/cover_fragment.xml +++ b/app/src/main/res/layout/cover_fragment.xml @@ -1,70 +1,43 @@ <?xml version="1.0" encoding="utf-8"?> - -<android.support.percent.PercentRelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="8dp"> + + <TextView + android:id="@+id/txtvPodcastTitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="0.25" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:textColor="?android:attr/textColorSecondary" + android:textIsSelectable="true" + tools:text="Podcast" /> <ImageView android:id="@+id/imgvCover" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_centerInParent="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="0.5" android:contentDescription="@string/cover_label" android:scaleType="fitCenter" - app:layout_aspectRatio="100%" - app:layout_widthPercent="82%" android:transitionName="coverTransition" tools:src="@android:drawable/sym_def_app_icon" /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:orientation="vertical" - android:layout_above="@id/imgvCover"> - - <TextView - android:id="@+id/txtvPodcastTitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:gravity="center" - android:maxLines="2" - android:ellipsize="end" - android:text="Podcast" - android:textIsSelectable="true" - android:textColor="?android:attr/textColorSecondary" /> - - </LinearLayout> - - <LinearLayout + <TextView + android:id="@+id/txtvEpisodeTitle" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_weight="0.25" + android:ellipsize="end" android:gravity="center" - android:orientation="vertical" - android:layout_below="@id/imgvCover"> - - <TextView - android:id="@+id/txtvEpisodeTitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:gravity="center" - android:maxLines="2" - android:ellipsize="end" - android:text="Episode" - android:textIsSelectable="true" - android:textColor="?android:attr/textColorPrimary" /> - - </LinearLayout> + android:maxLines="2" + android:textColor="?android:attr/textColorPrimary" + android:textIsSelectable="true" + tools:text="Episode" /> -</android.support.percent.PercentRelativeLayout> +</LinearLayout> diff --git a/app/src/main/res/layout/opml_import.xml b/app/src/main/res/layout/opml_import.xml index ac75cb8b3..9b2036228 100644 --- a/app/src/main/res/layout/opml_import.xml +++ b/app/src/main/res/layout/opml_import.xml @@ -15,14 +15,14 @@ tools:background="@android:color/darker_gray"> <TextView - android:id="@+id/txtvHeadingExplanation1" + android:id="@+id/txtvHeadingExplanation" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/AntennaPod.TextView.Heading" android:text="@string/txtvfeedurl_label"/> <TextView - android:id="@+id/txtvExplanation1" + android:id="@+id/txtvExplanation" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/opml_import_explanation_1" @@ -39,52 +39,21 @@ android:text="@string/choose_file_from_filesystem" /> <View - android:id="@+id/divider1" + android:id="@+id/divider" android:layout_width="fill_parent" android:layout_height="1dp" android:layout_margin="16dp" android:background="?android:attr/listDivider"/> <TextView - android:id="@+id/txtvHeadingExplanation2" + android:id="@+id/txtvHeadingExplanationOpenWith" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/AntennaPod.TextView.Heading" android:text="@string/txtvfeedurl_label"/> <TextView - android:id="@+id/txtvExplanation2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/opml_import_explanation_2" - android:textSize="@dimen/text_size_medium" - android:layout_marginTop="4dp" - tools:background="@android:color/holo_green_dark" /> - - <Button - android:id="@+id/butChooseFileFromExternal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginTop="8dp" - android:text="@string/choose_file_from_external_application" /> - - <View - android:id="@+id/divider2" - android:layout_width="fill_parent" - android:layout_height="1dp" - android:layout_margin="16dp" - android:background="?android:attr/listDivider"/> - - <TextView - android:id="@+id/txtvHeadingExplanation3" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/AntennaPod.TextView.Heading" - android:text="@string/txtvfeedurl_label"/> - - <TextView - android:id="@+id/txtvExplanation3" + android:id="@+id/txtvExplanationOpenWith" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/opml_import_explanation_3" diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml index bbaabcd8b..e21aac04e 100644 --- a/app/src/main/res/menu/episodes.xml +++ b/app/src/main/res/menu/episodes.xml @@ -6,7 +6,7 @@ <item android:id="@+id/action_search" android:icon="?attr/action_search" - custom:showAsAction="always" + custom:showAsAction="collapseActionView|ifRoom" custom:actionViewClass="android.support.v7.widget.SearchView" android:title="@string/search_label"/> @@ -14,7 +14,7 @@ android:id="@+id/refresh_item" android:title="@string/refresh_label" android:menuCategory="container" - custom:showAsAction="always" + custom:showAsAction="ifRoom" android:icon="?attr/navigation_refresh"/> <item diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml index 7b82cbef3..5844c9ff5 100644 --- a/app/src/main/res/menu/queue.xml +++ b/app/src/main/res/menu/queue.xml @@ -42,10 +42,10 @@ <menu> <item android:id="@+id/queue_sort_date_asc" - android:title="@string/ascending"/> + android:title="@string/sort_old_to_new"/> <item android:id="@+id/queue_sort_date_desc" - android:title="@string/descending"/> + android:title="@string/sort_new_to_old"/> </menu> </item> @@ -103,10 +103,10 @@ <menu> <item android:id="@+id/queue_sort_smart_shuffle_asc" - android:title="@string/ascending"/> + android:title="@string/sort_old_to_new"/> <item android:id="@+id/queue_sort_smart_shuffle_desc" - android:title="@string/descending"/> + android:title="@string/sort_new_to_old"/> </menu> </item> </menu> diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml index 9182df600..8b8efa023 100644 --- a/app/src/main/res/xml/preferences_playback.xml +++ b/app/src/main/res/xml/preferences_playback.xml @@ -46,7 +46,7 @@ app:useStockLayout="true"/> </PreferenceCategory> - <PreferenceCategory android:title="@string/buttons"> + <PreferenceCategory android:title="@string/playback_control"> <SwitchPreference android:defaultValue="false" android:enabled="true" @@ -71,6 +71,11 @@ android:key="prefPlaybackSpeedLauncher" android:summary="@string/pref_playback_speed_sum" android:title="@string/pref_playback_speed_title"/> + <SwitchPreference + android:defaultValue="false" + android:key="prefPlaybackTimeRespectsSpeed" + android:summary="@string/pref_playback_time_respects_speed_sum" + android:title="@string/pref_playback_time_respects_speed_title"/> </PreferenceCategory> <PreferenceCategory android:title="@string/queue_label"> |