diff options
author | ByteHamster <ByteHamster@users.noreply.github.com> | 2024-03-31 18:40:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-31 18:40:15 +0200 |
commit | edb440a5a9a05e24c344a71b272b9238217e9c55 (patch) | |
tree | 13623ca7d0dac052ac35d693aac940d0727c87f9 /app/src/main/java/de/danoeh/antennapod/activity | |
parent | 4e47691e70e85736c7eeb30ce02c73176e565a86 (diff) | |
download | AntennaPod-edb440a5a9a05e24c344a71b272b9238217e9c55.zip |
Restructure related UI classes together (#7044)
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod/activity')
7 files changed, 17 insertions, 1896 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java deleted file mode 100644 index e379b5eb2..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; -import com.google.android.material.snackbar.Snackbar; - -import androidx.annotation.NonNull; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ShareCompat; -import androidx.core.content.FileProvider; - - -import android.view.Menu; -import android.view.MenuItem; -import android.widget.TextView; - - -import de.danoeh.antennapod.ui.common.ThemeSwitcher; -import de.danoeh.antennapod.error.CrashReportWriter; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.IntentUtils; -import org.apache.commons.io.IOUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.Charset; - -/** - * Displays the 'crash report' screen - */ -public class BugReportActivity extends AppCompatActivity { - private static final String TAG = "BugReportActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(ThemeSwitcher.getTheme(this)); - super.onCreate(savedInstanceState); - getSupportActionBar().setDisplayShowHomeEnabled(true); - setContentView(R.layout.bug_report); - - String stacktrace = "No crash report recorded"; - try { - File crashFile = CrashReportWriter.getFile(); - if (crashFile.exists()) { - stacktrace = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8")); - } else { - Log.d(TAG, stacktrace); - } - } catch (IOException e) { - e.printStackTrace(); - } - - TextView crashDetailsTextView = findViewById(R.id.crash_report_logs); - crashDetailsTextView.setText(CrashReportWriter.getSystemInfo() + "\n\n" + stacktrace); - - findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> IntentUtils.openInBrowser( - BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues")); - - findViewById(R.id.btn_copy_log).setOnClickListener(v -> { - ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsTextView.getText()); - clipboard.setPrimaryClip(clip); - if (Build.VERSION.SDK_INT < 32) { - Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, - Snackbar.LENGTH_SHORT).show(); - } - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bug_report_options, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.export_logcat) { - MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(this); - alertBuilder.setMessage(R.string.confirm_export_log_dialog_message); - alertBuilder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { - exportLog(); - dialog.dismiss(); - }); - alertBuilder.setNegativeButton(R.string.cancel_label, null); - alertBuilder.show(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void exportLog() { - try { - File filename = new File(UserPreferences.getDataFolder(null), "full-logs.txt"); - String cmd = "logcat -d -f " + filename.getAbsolutePath(); - Runtime.getRuntime().exec(cmd); - //share file - try { - String authority = getString(R.string.provider_authority); - Uri fileUri = FileProvider.getUriForFile(this, authority, filename); - - new ShareCompat.IntentBuilder(this) - .setType("text/*") - .addStream(fileUri) - .setChooserTitle(R.string.share_file_label) - .startChooser(); - } catch (Exception e) { - e.printStackTrace(); - int strResId = R.string.log_file_share_exception; - Snackbar.make(findViewById(android.R.id.content), strResId, Snackbar.LENGTH_LONG) - .show(); - } - } catch (IOException e) { - e.printStackTrace(); - Snackbar.make(findViewById(android.R.id.content), e.getMessage(), Snackbar.LENGTH_LONG).show(); - } - } - - -} 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 32884c9c9..fd4007736 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -40,23 +40,23 @@ import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager; import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink; import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter; import de.danoeh.antennapod.ui.common.ThemeSwitcher; -import de.danoeh.antennapod.dialog.rating.RatingDialogManager; +import de.danoeh.antennapod.ui.screen.rating.RatingDialogManager; import de.danoeh.antennapod.event.EpisodeDownloadEvent; import de.danoeh.antennapod.event.FeedUpdateRunningEvent; import de.danoeh.antennapod.event.MessageEvent; -import de.danoeh.antennapod.fragment.AddFeedFragment; -import de.danoeh.antennapod.fragment.AllEpisodesFragment; -import de.danoeh.antennapod.fragment.AudioPlayerFragment; -import de.danoeh.antennapod.fragment.CompletedDownloadsFragment; -import de.danoeh.antennapod.fragment.DownloadLogFragment; -import de.danoeh.antennapod.fragment.FeedItemlistFragment; -import de.danoeh.antennapod.fragment.InboxFragment; -import de.danoeh.antennapod.fragment.NavDrawerFragment; -import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; -import de.danoeh.antennapod.fragment.QueueFragment; -import de.danoeh.antennapod.fragment.SearchFragment; -import de.danoeh.antennapod.fragment.SubscriptionFragment; -import de.danoeh.antennapod.fragment.TransitionEffect; +import de.danoeh.antennapod.ui.screen.AddFeedFragment; +import de.danoeh.antennapod.ui.screen.AllEpisodesFragment; +import de.danoeh.antennapod.ui.screen.playback.audio.AudioPlayerFragment; +import de.danoeh.antennapod.ui.screen.download.CompletedDownloadsFragment; +import de.danoeh.antennapod.ui.screen.download.DownloadLogFragment; +import de.danoeh.antennapod.ui.screen.feed.FeedItemlistFragment; +import de.danoeh.antennapod.ui.screen.InboxFragment; +import de.danoeh.antennapod.ui.screen.drawer.NavDrawerFragment; +import de.danoeh.antennapod.ui.screen.PlaybackHistoryFragment; +import de.danoeh.antennapod.ui.screen.queue.QueueFragment; +import de.danoeh.antennapod.ui.screen.SearchFragment; +import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment; +import de.danoeh.antennapod.ui.TransitionEffect; import de.danoeh.antennapod.model.download.DownloadStatus; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.playback.cast.CastEnabledActivity; @@ -65,8 +65,8 @@ import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; import de.danoeh.antennapod.ui.common.ThemeUtils; import de.danoeh.antennapod.ui.discovery.DiscoveryFragment; -import de.danoeh.antennapod.ui.home.HomeFragment; -import de.danoeh.antennapod.view.LockableBottomSheetBehavior; +import de.danoeh.antennapod.ui.screen.home.HomeFragment; +import de.danoeh.antennapod.ui.view.LockableBottomSheetBehavior; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; import org.greenrobot.eventbus.EventBus; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java deleted file mode 100644 index 1f3963d46..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ /dev/null @@ -1,713 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.LightingColorFilter; -import android.os.Bundle; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.ForegroundColorSpan; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import androidx.appcompat.app.AppCompatActivity; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.google.android.material.snackbar.Snackbar; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter; -import de.danoeh.antennapod.net.download.service.feed.remote.Downloader; -import de.danoeh.antennapod.net.download.service.feed.remote.HttpDownloader; -import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; -import de.danoeh.antennapod.ui.common.ThemeSwitcher; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestCreator; -import de.danoeh.antennapod.net.discovery.FeedUrlNotFoundException; -import de.danoeh.antennapod.storage.database.FeedDatabaseWriter; -import de.danoeh.antennapod.playback.service.PlaybackServiceInterface; -import de.danoeh.antennapod.core.util.DownloadErrorLabel; -import de.danoeh.antennapod.databinding.EditTextDialogBinding; -import de.danoeh.antennapod.databinding.OnlinefeedviewHeaderBinding; -import de.danoeh.antennapod.event.EpisodeDownloadEvent; -import de.danoeh.antennapod.event.FeedListUpdateEvent; -import de.danoeh.antennapod.event.PlayerStatusEvent; -import de.danoeh.antennapod.storage.preferences.PlaybackPreferences; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.model.download.DownloadRequest; -import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.storage.database.DBReader; -import de.danoeh.antennapod.storage.database.DBWriter; -import de.danoeh.antennapod.net.discovery.CombinedSearcher; -import de.danoeh.antennapod.net.discovery.PodcastSearchResult; -import de.danoeh.antennapod.net.discovery.PodcastSearcherRegistry; -import de.danoeh.antennapod.parser.feed.FeedHandler; -import de.danoeh.antennapod.parser.feed.FeedHandlerResult; -import de.danoeh.antennapod.model.download.DownloadError; -import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.net.common.UrlChecker; -import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer; -import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; -import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedPreferences; -import de.danoeh.antennapod.model.playback.RemoteMedia; -import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException; -import de.danoeh.antennapod.ui.common.ThemeUtils; -import de.danoeh.antennapod.ui.glide.FastBlurTransformation; -import de.danoeh.antennapod.ui.preferences.screen.synchronization.AuthenticationDialog; -import io.reactivex.Maybe; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.observers.DisposableMaybeObserver; -import io.reactivex.schedulers.Schedulers; -import org.apache.commons.lang3.StringUtils; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter.ARG_FEEDURL; -import static de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter.ARG_STARTED_FROM_SEARCH; -import static de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter.ARG_WAS_MANUAL_URL; - -/** - * Downloads a feed from a feed URL and parses it. Subclasses can display the - * feed object that was parsed. This activity MUST be started with a given URL - * or an Exception will be thrown. - * <p/> - * If the feed cannot be downloaded or parsed, an error dialog will be displayed - * and the activity will finish as soon as the error dialog is closed. - */ -public class OnlineFeedViewActivity extends AppCompatActivity { - - private static final int RESULT_ERROR = 2; - private static final String TAG = "OnlineFeedViewActivity"; - private static final String PREFS = "OnlineFeedViewActivityPreferences"; - private static final String PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload"; - private static final int DESCRIPTION_MAX_LINES_COLLAPSED = 4; - - private volatile List<Feed> feeds; - private String selectedDownloadUrl; - private Downloader downloader; - private String username = null; - private String password = null; - - private boolean isPaused; - private boolean didPressSubscribe = false; - private boolean isFeedFoundBySearch = false; - - private Dialog dialog; - - private Disposable download; - private Disposable parser; - private Disposable updater; - - private OnlinefeedviewHeaderBinding headerBinding; - private OnlinefeedviewActivityBinding viewBinding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(ThemeSwitcher.getTranslucentTheme(this)); - super.onCreate(savedInstanceState); - - viewBinding = OnlinefeedviewActivityBinding.inflate(getLayoutInflater()); - setContentView(viewBinding.getRoot()); - - viewBinding.transparentBackground.setOnClickListener(v -> finish()); - viewBinding.closeButton.setOnClickListener(view -> finish()); - viewBinding.card.setOnClickListener(null); - viewBinding.card.setCardBackgroundColor(ThemeUtils.getColorFromAttr(this, R.attr.colorSurface)); - headerBinding = OnlinefeedviewHeaderBinding.inflate(getLayoutInflater()); - - String feedUrl = null; - if (getIntent().hasExtra(ARG_FEEDURL)) { - feedUrl = getIntent().getStringExtra(ARG_FEEDURL); - } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)) { - feedUrl = getIntent().getStringExtra(Intent.EXTRA_TEXT); - } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { - feedUrl = getIntent().getDataString(); - } - - if (feedUrl == null) { - Log.e(TAG, "feedUrl is null."); - showNoPodcastFoundError(); - } else { - Log.d(TAG, "Activity was started with url " + feedUrl); - setLoadingLayout(); - // Remove subscribeonandroid.com from feed URL in order to subscribe to the actual feed URL - if (feedUrl.contains("subscribeonandroid.com")) { - feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))", ""); - } - if (savedInstanceState != null) { - username = savedInstanceState.getString("username"); - password = savedInstanceState.getString("password"); - } - lookupUrlAndDownload(feedUrl); - } - } - - private void showNoPodcastFoundError() { - runOnUiThread(() -> new MaterialAlertDialogBuilder(OnlineFeedViewActivity.this) - .setNeutralButton(android.R.string.ok, (dialog, which) -> finish()) - .setTitle(R.string.error_label) - .setMessage(R.string.null_value_podcast_error) - .setOnDismissListener(dialog1 -> { - setResult(RESULT_ERROR); - finish(); - }) - .show()); - } - - /** - * Displays a progress indicator. - */ - private void setLoadingLayout() { - viewBinding.progressBar.setVisibility(View.VISIBLE); - viewBinding.feedDisplayContainer.setVisibility(View.GONE); - } - - @Override - protected void onStart() { - super.onStart(); - isPaused = false; - EventBus.getDefault().register(this); - } - - @Override - protected void onStop() { - super.onStop(); - isPaused = true; - EventBus.getDefault().unregister(this); - if (downloader != null && !downloader.isFinished()) { - downloader.cancel(); - } - if(dialog != null && dialog.isShowing()) { - dialog.dismiss(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - if(updater != null) { - updater.dispose(); - } - if(download != null) { - download.dispose(); - } - if(parser != null) { - parser.dispose(); - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("username", username); - outState.putString("password", password); - } - - private void resetIntent(String url) { - Intent intent = new Intent(); - intent.putExtra(ARG_FEEDURL, url); - setIntent(intent); - } - - @Override - public void finish() { - super.finish(); - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } - - private void lookupUrlAndDownload(String url) { - download = PodcastSearcherRegistry.lookupUrl(url) - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.io()) - .subscribe(this::startFeedDownload, - error -> { - if (error instanceof FeedUrlNotFoundException) { - tryToRetrieveFeedUrlBySearch((FeedUrlNotFoundException) error); - } else { - showNoPodcastFoundError(); - Log.e(TAG, Log.getStackTraceString(error)); - } - }); - } - - private void tryToRetrieveFeedUrlBySearch(FeedUrlNotFoundException error) { - Log.d(TAG, "Unable to retrieve feed url, trying to retrieve feed url from search"); - String url = searchFeedUrlByTrackName(error.getTrackName(), error.getArtistName()); - if (url != null) { - Log.d(TAG, "Successfully retrieve feed url"); - isFeedFoundBySearch = true; - startFeedDownload(url); - } else { - showNoPodcastFoundError(); - Log.d(TAG, "Failed to retrieve feed url"); - } - } - - private String searchFeedUrlByTrackName(String trackName, String artistName) { - CombinedSearcher searcher = new CombinedSearcher(); - String query = trackName + " " + artistName; - List<PodcastSearchResult> results = searcher.search(query).blockingGet(); - for (PodcastSearchResult result : results) { - if (result.feedUrl != null && result.author != null - && result.author.equalsIgnoreCase(artistName) && result.title.equalsIgnoreCase(trackName)) { - return result.feedUrl; - - } - } - return null; - } - - private void startFeedDownload(String url) { - Log.d(TAG, "Starting feed download"); - selectedDownloadUrl = UrlChecker.prepareUrl(url); - DownloadRequest request = DownloadRequestCreator.create(new Feed(selectedDownloadUrl, null)) - .withAuthentication(username, password) - .withInitiatedByUser(true) - .build(); - - download = Observable.fromCallable(() -> { - feeds = DBReader.getFeedList(); - downloader = new HttpDownloader(request); - downloader.call(); - return downloader.getResult(); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(status -> checkDownloadResult(status, request.getDestination()), - error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - private void checkDownloadResult(@NonNull DownloadResult status, String destination) { - if (status.isSuccessful()) { - parseFeed(destination); - } else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) { - if (!isFinishing() && !isPaused) { - if (username != null && password != null) { - Toast.makeText(this, R.string.download_error_unauthorized, Toast.LENGTH_LONG).show(); - } - dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this, - R.string.authentication_notification_title, - downloader.getDownloadRequest().getSource()).create(); - dialog.show(); - } - } else { - showErrorDialog(getString(DownloadErrorLabel.from(status.getReason())), status.getReasonDetailed()); - } - } - - @Subscribe - public void onFeedListChanged(FeedListUpdateEvent event) { - updater = Observable.fromCallable(DBReader::getFeedList) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - feeds -> { - OnlineFeedViewActivity.this.feeds = feeds; - handleUpdatedFeedStatus(); - }, error -> Log.e(TAG, Log.getStackTraceString(error)) - ); - } - - @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - public void onEventMainThread(EpisodeDownloadEvent event) { - handleUpdatedFeedStatus(); - } - - private void parseFeed(String destination) { - Log.d(TAG, "Parsing feed"); - parser = Maybe.fromCallable(() -> doParseFeed(destination)) - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableMaybeObserver<FeedHandlerResult>() { - @Override - public void onSuccess(@NonNull FeedHandlerResult result) { - showFeedInformation(result.feed, result.alternateFeedUrls); - } - - @Override - public void onComplete() { - // Ignore null result: We showed the discovery dialog. - } - - @Override - public void onError(@NonNull Throwable error) { - showErrorDialog(error.getMessage(), ""); - Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error)); - } - }); - } - - /** - * Try to parse the feed. - * @return The FeedHandlerResult if successful. - * Null if unsuccessful but we started another attempt. - * @throws Exception If unsuccessful but we do not know a resolution. - */ - @Nullable - private FeedHandlerResult doParseFeed(String destination) throws Exception { - FeedHandler handler = new FeedHandler(); - Feed feed = new Feed(selectedDownloadUrl, null); - feed.setLocalFileUrl(destination); - File destinationFile = new File(destination); - try { - return handler.parseFeed(feed); - } catch (UnsupportedFeedtypeException e) { - Log.d(TAG, "Unsupported feed type detected"); - if ("html".equalsIgnoreCase(e.getRootElement())) { - boolean dialogShown = showFeedDiscoveryDialog(destinationFile, selectedDownloadUrl); - if (dialogShown) { - return null; // Should not display an error message - } else { - throw new UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html)); - } - } else { - throw e; - } - } catch (Exception e) { - Log.e(TAG, Log.getStackTraceString(e)); - throw e; - } finally { - boolean rc = destinationFile.delete(); - Log.d(TAG, "Deleted feed source file. Result: " + rc); - } - } - - /** - * Called when feed parsed successfully. - * This method is executed on the GUI thread. - */ - private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) { - viewBinding.progressBar.setVisibility(View.GONE); - viewBinding.feedDisplayContainer.setVisibility(View.VISIBLE); - if (isFeedFoundBySearch) { - int resId = R.string.no_feed_url_podcast_found_by_search; - Snackbar.make(findViewById(android.R.id.content), resId, Snackbar.LENGTH_LONG).show(); - } - - viewBinding.backgroundImage.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - - viewBinding.listView.addHeaderView(headerBinding.getRoot()); - viewBinding.listView.setSelector(android.R.color.transparent); - viewBinding.listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems())); - - if (StringUtils.isNotBlank(feed.getImageUrl())) { - Glide.with(this) - .load(feed.getImageUrl()) - .apply(new RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .fitCenter() - .dontAnimate()) - .into(viewBinding.coverImage); - Glide.with(this) - .load(feed.getImageUrl()) - .apply(new RequestOptions() - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .transform(new FastBlurTransformation()) - .dontAnimate()) - .into(viewBinding.backgroundImage); - } - - viewBinding.titleLabel.setText(feed.getTitle()); - viewBinding.authorLabel.setText(feed.getAuthor()); - headerBinding.txtvDescription.setText(HtmlToPlainText.getPlainText(feed.getDescription())); - - viewBinding.subscribeButton.setOnClickListener(v -> { - if (feedInFeedlist()) { - openFeed(); - } else { - FeedDatabaseWriter.updateFeed(this, feed, false); - didPressSubscribe = true; - handleUpdatedFeedStatus(); - } - }); - - viewBinding.stopPreviewButton.setOnClickListener(v -> { - PlaybackPreferences.writeNoMediaPlaying(); - IntentUtils.sendLocalBroadcast(this, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); - }); - - if (UserPreferences.isEnableAutodownload()) { - SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE); - viewBinding.autoDownloadCheckBox.setChecked(preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)); - } - - headerBinding.txtvDescription.setMaxLines(DESCRIPTION_MAX_LINES_COLLAPSED); - headerBinding.txtvDescription.setOnClickListener(v -> { - if (headerBinding.txtvDescription.getMaxLines() > DESCRIPTION_MAX_LINES_COLLAPSED) { - headerBinding.txtvDescription.setMaxLines(DESCRIPTION_MAX_LINES_COLLAPSED); - } else { - headerBinding.txtvDescription.setMaxLines(2000); - } - }); - - if (alternateFeedUrls.isEmpty()) { - viewBinding.alternateUrlsSpinner.setVisibility(View.GONE); - } else { - viewBinding.alternateUrlsSpinner.setVisibility(View.VISIBLE); - - final List<String> alternateUrlsList = new ArrayList<>(); - final List<String> alternateUrlsTitleList = new ArrayList<>(); - - alternateUrlsList.add(feed.getDownloadUrl()); - alternateUrlsTitleList.add(feed.getTitle()); - - - alternateUrlsList.addAll(alternateFeedUrls.keySet()); - for (String url : alternateFeedUrls.keySet()) { - alternateUrlsTitleList.add(alternateFeedUrls.get(url)); - } - - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, - R.layout.alternate_urls_item, alternateUrlsTitleList) { - @Override - public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - // reusing the old view causes a visual bug on Android <= 10 - return super.getDropDownView(position, null, parent); - } - }; - - adapter.setDropDownViewResource(R.layout.alternate_urls_dropdown_item); - viewBinding.alternateUrlsSpinner.setAdapter(adapter); - viewBinding.alternateUrlsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - selectedDownloadUrl = alternateUrlsList.get(position); - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - - } - }); - } - handleUpdatedFeedStatus(); - } - - private void openFeed() { - // feed.getId() is always 0, we have to retrieve the id from the feed list from the database - MainActivityStarter mainActivityStarter = new MainActivityStarter(this); - mainActivityStarter.withOpenFeed(getFeedId()); - if (getIntent().getBooleanExtra(ARG_STARTED_FROM_SEARCH, false)) { - mainActivityStarter.withAddToBackStack(); - } - finish(); - startActivity(mainActivityStarter.getIntent()); - } - - private void handleUpdatedFeedStatus() { - if (DownloadServiceInterface.get().isDownloadingEpisode(selectedDownloadUrl)) { - viewBinding.subscribeButton.setEnabled(false); - viewBinding.subscribeButton.setText(R.string.subscribing_label); - } else if (feedInFeedlist()) { - viewBinding.subscribeButton.setEnabled(true); - viewBinding.subscribeButton.setText(R.string.open_podcast); - if (didPressSubscribe) { - didPressSubscribe = false; - - Feed feed1 = DBReader.getFeed(getFeedId()); - FeedPreferences feedPreferences = feed1.getPreferences(); - if (UserPreferences.isEnableAutodownload()) { - boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked(); - feedPreferences.setAutoDownload(autoDownload); - - SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE); - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload); - editor.apply(); - } - if (username != null) { - feedPreferences.setUsername(username); - feedPreferences.setPassword(password); - } - DBWriter.setFeedPreferences(feedPreferences); - - openFeed(); - } - } else { - viewBinding.subscribeButton.setEnabled(true); - viewBinding.subscribeButton.setText(R.string.subscribe_label); - if (UserPreferences.isEnableAutodownload()) { - viewBinding.autoDownloadCheckBox.setVisibility(View.VISIBLE); - } - } - } - - private boolean feedInFeedlist() { - return getFeedId() != 0; - } - - private long getFeedId() { - if (feeds == null) { - return 0; - } - for (Feed f : feeds) { - if (f.getDownloadUrl().equals(selectedDownloadUrl)) { - return f.getId(); - } - } - return 0; - } - - @UiThread - private void showErrorDialog(String errorMsg, String details) { - if (!isFinishing() && !isPaused) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(R.string.error_label); - if (errorMsg != null) { - String total = errorMsg + "\n\n" + details; - SpannableString errorMessage = new SpannableString(total); - errorMessage.setSpan(new ForegroundColorSpan(0x88888888), - errorMsg.length(), total.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - builder.setMessage(errorMessage); - } else { - builder.setMessage(R.string.download_error_error_unknown); - } - builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.cancel()); - if (getIntent().getBooleanExtra(ARG_WAS_MANUAL_URL, false)) { - builder.setNeutralButton(R.string.edit_url_menu, (dialog, which) -> editUrl()); - } - builder.setOnCancelListener(dialog -> { - setResult(RESULT_ERROR); - finish(); - }); - if (dialog != null && dialog.isShowing()) { - dialog.dismiss(); - } - dialog = builder.show(); - } - } - - private void editUrl() { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(R.string.edit_url_menu); - final EditTextDialogBinding dialogBinding = EditTextDialogBinding.inflate(getLayoutInflater()); - if (downloader != null) { - dialogBinding.urlEditText.setText(downloader.getDownloadRequest().getSource()); - } - builder.setView(dialogBinding.getRoot()); - builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { - setLoadingLayout(); - lookupUrlAndDownload(dialogBinding.urlEditText.getText().toString()); - }); - builder.setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel()); - builder.setOnCancelListener(dialog1 -> { - setResult(RESULT_ERROR); - finish(); - }); - builder.show(); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void playbackStateChanged(PlayerStatusEvent event) { - boolean isPlayingPreview = - PlaybackPreferences.getCurrentlyPlayingMediaType() == RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA; - viewBinding.stopPreviewButton.setVisibility(isPlayingPreview ? View.VISIBLE : View.GONE); - } - - /** - * - * @return true if a FeedDiscoveryDialog is shown, false otherwise (e.g., due to no feed found). - */ - private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) { - FeedDiscoverer fd = new FeedDiscoverer(); - final Map<String, String> urlsMap; - try { - urlsMap = fd.findLinks(feedFile, baseUrl); - if (urlsMap == null || urlsMap.isEmpty()) { - return false; - } - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - if (isPaused || isFinishing()) { - return false; - } - - final List<String> titles = new ArrayList<>(); - - final List<String> urls = new ArrayList<>(urlsMap.keySet()); - for (String url : urls) { - titles.add(urlsMap.get(url)); - } - - if (urls.size() == 1) { - // Skip dialog and display the item directly - resetIntent(urls.get(0)); - startFeedDownload(urls.get(0)); - return true; - } - - final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this, - R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles); - DialogInterface.OnClickListener onClickListener = (dialog, which) -> { - String selectedUrl = urls.get(which); - dialog.dismiss(); - resetIntent(selectedUrl); - startFeedDownload(selectedUrl); - }; - - MaterialAlertDialogBuilder ab = new MaterialAlertDialogBuilder(OnlineFeedViewActivity.this) - .setTitle(R.string.feeds_label) - .setCancelable(true) - .setOnCancelListener(dialog -> finish()) - .setAdapter(adapter, onClickListener); - - runOnUiThread(() -> { - if(dialog != null && dialog.isShowing()) { - dialog.dismiss(); - } - dialog = ab.show(); - }); - return true; - } - - private class FeedViewAuthenticationDialog extends AuthenticationDialog { - - private final String feedUrl; - - FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) { - super(context, titleRes, true, username, password); - this.feedUrl = feedUrl; - } - - @Override - protected void onCancelled() { - super.onCancelled(); - finish(); - } - - @Override - protected void onConfirmed(String username, String password) { - OnlineFeedViewActivity.this.username = username; - OnlineFeedViewActivity.this.password = password; - startFeedDownload(feedUrl); - } - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PlaybackSpeedDialogActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PlaybackSpeedDialogActivity.java deleted file mode 100644 index 37f13272a..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/PlaybackSpeedDialogActivity.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.danoeh.antennapod.activity; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import android.content.DialogInterface; -import android.os.Bundle; - -import de.danoeh.antennapod.ui.common.ThemeSwitcher; -import de.danoeh.antennapod.dialog.VariableSpeedDialog; - -public class PlaybackSpeedDialogActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(ThemeSwitcher.getTranslucentTheme(this)); - super.onCreate(savedInstanceState); - VariableSpeedDialog speedDialog = new InnerVariableSpeedDialog(); - speedDialog.show(getSupportFragmentManager(), null); - } - - public static class InnerVariableSpeedDialog extends VariableSpeedDialog { - @Override - public void onDismiss(@NonNull DialogInterface dialog) { - super.onDismiss(dialog); - getActivity().finish(); - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java deleted file mode 100644 index aa3b05715..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ /dev/null @@ -1,194 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.util.Log; -import android.view.MenuItem; - -import android.view.View; -import android.view.inputmethod.InputMethodManager; - -import androidx.appcompat.app.ActionBar; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import androidx.appcompat.app.AppCompatActivity; -import androidx.preference.PreferenceFragmentCompat; - -import com.bytehamster.lib.preferencesearch.SearchPreferenceResult; -import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener; - -import com.google.android.material.snackbar.Snackbar; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.ui.common.ThemeSwitcher; - -import de.danoeh.antennapod.event.MessageEvent; -import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.DownloadsPreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.SwipePreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragment; -import de.danoeh.antennapod.ui.preferences.screen.AutoDownloadPreferencesFragment; -import de.danoeh.antennapod.ui.preferences.screen.NotificationPreferencesFragment; -import de.danoeh.antennapod.ui.preferences.screen.synchronization.SynchronizationPreferencesFragment; -import de.danoeh.antennapod.ui.preferences.databinding.SettingsActivityBinding; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -/** - * PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see - * PreferenceController. - */ -public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener { - private static final String FRAGMENT_TAG = "tag_preferences"; - public static final String OPEN_AUTO_DOWNLOAD_SETTINGS = "OpenAutoDownloadSettings"; - private SettingsActivityBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(ThemeSwitcher.getTheme(this)); - super.onCreate(savedInstanceState); - - ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayHomeAsUpEnabled(true); - } - - binding = SettingsActivityBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - if (getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG) == null) { - getSupportFragmentManager().beginTransaction() - .replace(binding.settingsContainer.getId(), new MainPreferencesFragment(), FRAGMENT_TAG) - .commit(); - } - Intent intent = getIntent(); - if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) { - openScreen(R.xml.preferences_autodownload); - } - } - - private PreferenceFragmentCompat getPreferenceScreen(int screen) { - PreferenceFragmentCompat prefFragment = null; - - if (screen == R.xml.preferences_user_interface) { - prefFragment = new UserInterfacePreferencesFragment(); - } else if (screen == R.xml.preferences_downloads) { - prefFragment = new DownloadsPreferencesFragment(); - } else if (screen == R.xml.preferences_import_export) { - prefFragment = new ImportExportPreferencesFragment(); - } else if (screen == R.xml.preferences_autodownload) { - prefFragment = new AutoDownloadPreferencesFragment(); - } else if (screen == R.xml.preferences_synchronization) { - prefFragment = new SynchronizationPreferencesFragment(); - } else if (screen == R.xml.preferences_playback) { - prefFragment = new PlaybackPreferencesFragment(); - } else if (screen == R.xml.preferences_notifications) { - prefFragment = new NotificationPreferencesFragment(); - } else if (screen == R.xml.preferences_swipe) { - prefFragment = new SwipePreferencesFragment(); - } - return prefFragment; - } - - public static int getTitleOfPage(int preferences) { - if (preferences == R.xml.preferences_downloads) { - return R.string.downloads_pref; - } else if (preferences == R.xml.preferences_autodownload) { - return R.string.pref_automatic_download_title; - } else if (preferences == R.xml.preferences_playback) { - return R.string.playback_pref; - } else if (preferences == R.xml.preferences_import_export) { - return R.string.import_export_pref; - } else if (preferences == R.xml.preferences_user_interface) { - return R.string.user_interface_label; - } else if (preferences == R.xml.preferences_synchronization) { - return R.string.synchronization_pref; - } else if (preferences == R.xml.preferences_notifications) { - return R.string.notification_pref_fragment; - } else if (preferences == R.xml.feed_settings) { - return R.string.feed_settings_label; - } else if (preferences == R.xml.preferences_swipe) { - return R.string.swipeactions_label; - } - return R.string.settings_label; - } - - public PreferenceFragmentCompat openScreen(int screen) { - PreferenceFragmentCompat fragment = getPreferenceScreen(screen); - if (screen == R.xml.preferences_notifications && Build.VERSION.SDK_INT >= 26) { - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); - startActivity(intent); - } else { - getSupportFragmentManager().beginTransaction() - .replace(binding.settingsContainer.getId(), fragment) - .addToBackStack(getString(getTitleOfPage(screen))).commit(); - } - - - return fragment; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - if (getSupportFragmentManager().getBackStackEntryCount() == 0) { - finish(); - } else { - InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - View view = getCurrentFocus(); - //If no view currently has focus, create a new one, just so we can grab a window token from it - if (view == null) { - view = new View(this); - } - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - getSupportFragmentManager().popBackStack(); - } - return true; - } - return false; - } - - @Override - public void onSearchResultClicked(SearchPreferenceResult result) { - int screen = result.getResourceFile(); - if (screen == R.xml.feed_settings) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(R.string.feed_settings_label); - builder.setMessage(R.string.pref_feed_settings_dialog_msg); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } else if (screen == R.xml.preferences_notifications) { - openScreen(screen); - } else { - PreferenceFragmentCompat fragment = openScreen(result.getResourceFile()); - result.highlight(fragment); - } - } - - @Override - protected void onStart() { - super.onStart(); - EventBus.getDefault().register(this); - } - - @Override - protected void onStop() { - super.onStop(); - EventBus.getDefault().unregister(this); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(MessageEvent event) { - Log.d(FRAGMENT_TAG, "onEvent(" + event + ")"); - Snackbar s = Snackbar.make(binding.getRoot(), event.message, Snackbar.LENGTH_LONG); - if (event.action != null) { - s.setAction(event.actionText, v -> event.action.accept(this)); - } - s.show(); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java index 43da309d7..4ea33ca3e 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java @@ -7,7 +7,7 @@ import android.os.Bundle; import android.view.View; import android.widget.Toast; import androidx.annotation.Nullable; -import de.danoeh.antennapod.error.CrashReportWriter; +import de.danoeh.antennapod.CrashReportWriter; import de.danoeh.antennapod.storage.database.PodDBAdapter; import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java deleted file mode 100644 index 313d97248..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ /dev/null @@ -1,815 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; -import android.media.AudioManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.util.Pair; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.AnimationUtils; -import android.view.animation.ScaleAnimation; -import android.widget.EditText; -import android.widget.FrameLayout; -import android.widget.SeekBar; -import androidx.annotation.Nullable; -import androidx.interpolator.view.animation.FastOutSlowInInterpolator; -import com.bumptech.glide.Glide; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.dialog.MediaPlayerErrorDialog; -import de.danoeh.antennapod.dialog.VariableSpeedDialog; -import de.danoeh.antennapod.event.MessageEvent; -import de.danoeh.antennapod.event.playback.BufferUpdateEvent; -import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; -import de.danoeh.antennapod.event.PlayerErrorEvent; -import de.danoeh.antennapod.event.playback.PlaybackServiceEvent; -import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent; -import de.danoeh.antennapod.fragment.ChaptersFragment; -import de.danoeh.antennapod.playback.service.PlaybackController; -import de.danoeh.antennapod.playback.service.PlaybackService; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.storage.database.DBReader; -import de.danoeh.antennapod.storage.database.DBWriter; -import de.danoeh.antennapod.ui.common.Converter; -import de.danoeh.antennapod.core.util.FeedItemUtil; -import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.core.util.ShareUtils; -import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; -import de.danoeh.antennapod.databinding.VideoplayerActivityBinding; -import de.danoeh.antennapod.dialog.PlaybackControlsDialog; -import de.danoeh.antennapod.dialog.ShareDialog; -import de.danoeh.antennapod.dialog.SkipPreferenceDialog; -import de.danoeh.antennapod.dialog.SleepTimerDialog; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; -import de.danoeh.antennapod.model.playback.Playable; -import de.danoeh.antennapod.playback.base.PlayerStatus; -import de.danoeh.antennapod.playback.cast.CastEnabledActivity; -import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; -import de.danoeh.antennapod.ui.episodes.TimeSpeedConverter; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import org.apache.commons.lang3.StringUtils; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -/** - * Activity for playing video files. - */ -public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.OnSeekBarChangeListener { - private static final String TAG = "VideoplayerActivity"; - - /** - * True if video controls are currently visible. - */ - private boolean videoControlsShowing = true; - private boolean videoSurfaceCreated = false; - private boolean destroyingDueToReload = false; - private long lastScreenTap = 0; - private final Handler videoControlsHider = new Handler(Looper.getMainLooper()); - private VideoplayerActivityBinding viewBinding; - private PlaybackController controller; - private boolean showTimeLeft = false; - private boolean isFavorite = false; - private boolean switchToAudioOnly = false; - private Disposable disposable; - private float prog; - - @Override - protected void onCreate(Bundle savedInstanceState) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); - // has to be called before setting layout content - supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); - setTheme(R.style.Theme_AntennaPod_VideoPlayer); - super.onCreate(savedInstanceState); - - Log.d(TAG, "onCreate()"); - - getWindow().setFormat(PixelFormat.TRANSPARENT); - viewBinding = VideoplayerActivityBinding.inflate(LayoutInflater.from(this)); - setContentView(viewBinding.getRoot()); - setupView(); - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(0x80000000)); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } - - @Override - protected void onResume() { - super.onResume(); - switchToAudioOnly = false; - if (PlaybackService.isCasting()) { - Intent intent = PlaybackService.getPlayerActivityIntent(this); - if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { - destroyingDueToReload = true; - finish(); - startActivity(intent); - } - } - } - - @Override - protected void onStop() { - if (controller != null) { - controller.release(); - controller = null; // prevent leak - } - if (disposable != null) { - disposable.dispose(); - } - EventBus.getDefault().unregister(this); - super.onStop(); - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - videoControlsHider.removeCallbacks(hideVideoControls); - } - // Controller released; we will not receive buffering updates - viewBinding.progressBar.setVisibility(View.GONE); - } - - @Override - public void onUserLeaveHint() { - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - compatEnterPictureInPicture(); - } - } - - @Override - protected void onStart() { - super.onStart(); - controller = newPlaybackController(); - controller.init(); - loadMediaInfo(); - onPositionObserverUpdate(); - EventBus.getDefault().register(this); - } - - @Override - protected void onPause() { - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { - controller.pause(); - } - } - super.onPause(); - } - - @Override - public void onTrimMemory(int level) { - super.onTrimMemory(level); - Glide.get(this).trimMemory(level); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - Glide.get(this).clearMemory(); - } - - private PlaybackController newPlaybackController() { - return new PlaybackController(this) { - @Override - protected void updatePlayButtonShowsPlay(boolean showPlay) { - viewBinding.playButton.setIsShowPlay(showPlay); - if (showPlay) { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - setupVideoAspectRatio(); - if (videoSurfaceCreated && controller != null) { - Log.d(TAG, "Videosurface already created, setting videosurface now"); - controller.setVideoSurface(viewBinding.videoView.getHolder()); - } - } - } - - @Override - public void loadMediaInfo() { - VideoplayerActivity.this.loadMediaInfo(); - } - - @Override - public void onPlaybackEnd() { - finish(); - } - }; - } - - @Subscribe(threadMode = ThreadMode.MAIN) - @SuppressWarnings("unused") - public void bufferUpdate(BufferUpdateEvent event) { - if (event.hasStarted()) { - viewBinding.progressBar.setVisibility(View.VISIBLE); - } else if (event.hasEnded()) { - viewBinding.progressBar.setVisibility(View.INVISIBLE); - } else { - viewBinding.sbPosition.setSecondaryProgress((int) (event.getProgress() * viewBinding.sbPosition.getMax())); - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - @SuppressWarnings("unused") - public void sleepTimerUpdate(SleepTimerUpdatedEvent event) { - if (event.isCancelled() || event.wasJustEnabled()) { - supportInvalidateOptionsMenu(); - } - } - - protected void loadMediaInfo() { - Log.d(TAG, "loadMediaInfo()"); - if (controller == null || controller.getMedia() == null) { - return; - } - if (controller.getStatus() == PlayerStatus.PLAYING && !controller.isPlayingVideoLocally()) { - Log.d(TAG, "Closing, no longer video"); - destroyingDueToReload = true; - finish(); - new MainActivityStarter(this).withOpenPlayer().start(); - return; - } - showTimeLeft = UserPreferences.shouldShowRemainingTime(); - onPositionObserverUpdate(); - checkFavorite(); - Playable media = controller.getMedia(); - if (media != null) { - getSupportActionBar().setSubtitle(media.getEpisodeTitle()); - getSupportActionBar().setTitle(media.getFeedTitle()); - } - } - - protected void setupView() { - showTimeLeft = UserPreferences.shouldShowRemainingTime(); - Log.d("timeleft", showTimeLeft ? "true" : "false"); - viewBinding.durationLabel.setOnClickListener(v -> { - showTimeLeft = !showTimeLeft; - Playable media = controller.getMedia(); - if (media == null) { - return; - } - - TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier()); - String length; - if (showTimeLeft) { - int remainingTime = converter.convert(media.getDuration() - media.getPosition()); - length = "-" + Converter.getDurationStringLong(remainingTime); - } else { - int duration = converter.convert(media.getDuration()); - length = Converter.getDurationStringLong(duration); - } - viewBinding.durationLabel.setText(length); - - UserPreferences.setShowRemainTimeSetting(showTimeLeft); - Log.d("timeleft on click", showTimeLeft ? "true" : "false"); - }); - - viewBinding.sbPosition.setOnSeekBarChangeListener(this); - viewBinding.rewindButton.setOnClickListener(v -> onRewind()); - viewBinding.rewindButton.setOnLongClickListener(v -> { - SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this, - SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null); - return true; - }); - viewBinding.playButton.setIsVideoScreen(true); - viewBinding.playButton.setOnClickListener(v -> onPlayPause()); - viewBinding.fastForwardButton.setOnClickListener(v -> onFastForward()); - viewBinding.fastForwardButton.setOnLongClickListener(v -> { - SkipPreferenceDialog.showSkipPreference(VideoplayerActivity.this, - SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null); - return false; - }); - // To suppress touches directly below the slider - viewBinding.bottomControlsContainer.setOnTouchListener((view, motionEvent) -> true); - viewBinding.bottomControlsContainer.setFitsSystemWindows(true); - viewBinding.videoView.getHolder().addCallback(surfaceHolderCallback); - viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - - setupVideoControlsToggler(); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - viewBinding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched); - viewBinding.videoPlayerContainer.getViewTreeObserver().addOnGlobalLayoutListener(() -> - viewBinding.videoView.setAvailableSize( - viewBinding.videoPlayerContainer.getWidth(), viewBinding.videoPlayerContainer.getHeight())); - } - - private final Runnable hideVideoControls = () -> { - if (videoControlsShowing) { - Log.d(TAG, "Hiding video controls"); - getSupportActionBar().hide(); - hideVideoControls(true); - videoControlsShowing = false; - } - }; - - private final View.OnTouchListener onVideoviewTouched = (v, event) -> { - if (event.getAction() != MotionEvent.ACTION_DOWN) { - return false; - } - if (PictureInPictureUtil.isInPictureInPictureMode(this)) { - return true; - } - videoControlsHider.removeCallbacks(hideVideoControls); - - if (System.currentTimeMillis() - lastScreenTap < 300) { - if (event.getX() > v.getMeasuredWidth() / 2.0f) { - onFastForward(); - showSkipAnimation(true); - } else { - onRewind(); - showSkipAnimation(false); - } - if (videoControlsShowing) { - getSupportActionBar().hide(); - hideVideoControls(false); - videoControlsShowing = false; - } - return true; - } - - toggleVideoControlsVisibility(); - if (videoControlsShowing) { - setupVideoControlsToggler(); - } - - lastScreenTap = System.currentTimeMillis(); - return true; - }; - - private void showSkipAnimation(boolean isForward) { - AnimationSet skipAnimation = new AnimationSet(true); - skipAnimation.addAnimation(new ScaleAnimation(1f, 2f, 1f, 2f, - Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)); - skipAnimation.addAnimation(new AlphaAnimation(1f, 0f)); - skipAnimation.setFillAfter(false); - skipAnimation.setDuration(800); - - FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewBinding.skipAnimationImage.getLayoutParams(); - if (isForward) { - viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white); - params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; - } else { - viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white); - params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; - } - - viewBinding.skipAnimationImage.setVisibility(View.VISIBLE); - viewBinding.skipAnimationImage.setLayoutParams(params); - viewBinding.skipAnimationImage.startAnimation(skipAnimation); - skipAnimation.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - viewBinding.skipAnimationImage.setVisibility(View.GONE); - } - - @Override - public void onAnimationRepeat(Animation animation) { - - } - }); - } - - private void setupVideoControlsToggler() { - videoControlsHider.removeCallbacks(hideVideoControls); - videoControlsHider.postDelayed(hideVideoControls, 2500); - } - - private void setupVideoAspectRatio() { - if (videoSurfaceCreated && controller != null) { - Pair<Integer, Integer> videoSize = controller.getVideoSize(); - if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) { - Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second); - viewBinding.videoView.setVideoSize(videoSize.first, videoSize.second); - } else { - Log.e(TAG, "Could not determine video size"); - } - } - } - - private void toggleVideoControlsVisibility() { - if (videoControlsShowing) { - getSupportActionBar().hide(); - hideVideoControls(true); - } else { - getSupportActionBar().show(); - showVideoControls(); - } - videoControlsShowing = !videoControlsShowing; - } - - void onRewind() { - if (controller == null) { - return; - } - int curr = controller.getPosition(); - controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); - setupVideoControlsToggler(); - } - - void onPlayPause() { - if (controller == null) { - return; - } - controller.playPause(); - setupVideoControlsToggler(); - } - - void onFastForward() { - if (controller == null) { - return; - } - int curr = controller.getPosition(); - controller.seekTo(curr + UserPreferences.getFastForwardSecs() * 1000); - setupVideoControlsToggler(); - } - - private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - holder.setFixedSize(width, height); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.d(TAG, "Videoview holder created"); - videoSurfaceCreated = true; - if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { - controller.setVideoSurface(holder); - } - setupVideoAspectRatio(); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - Log.d(TAG, "Videosurface was destroyed"); - videoSurfaceCreated = false; - if (controller != null && !destroyingDueToReload && !switchToAudioOnly) { - controller.notifyVideoSurfaceAbandoned(); - } - } - }; - - private void showVideoControls() { - viewBinding.bottomControlsContainer.setVisibility(View.VISIBLE); - viewBinding.controlsContainer.setVisibility(View.VISIBLE); - final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in); - if (animation != null) { - viewBinding.bottomControlsContainer.startAnimation(animation); - viewBinding.controlsContainer.startAnimation(animation); - } - viewBinding.videoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } - - private void hideVideoControls(boolean showAnimation) { - if (showAnimation) { - final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out); - if (animation != null) { - viewBinding.bottomControlsContainer.startAnimation(animation); - viewBinding.controlsContainer.startAnimation(animation); - } - } - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE - | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - viewBinding.bottomControlsContainer.setFitsSystemWindows(true); - - viewBinding.bottomControlsContainer.setVisibility(View.GONE); - viewBinding.controlsContainer.setVisibility(View.GONE); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(PlaybackPositionEvent event) { - onPositionObserverUpdate(); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onPlaybackServiceChanged(PlaybackServiceEvent event) { - if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) { - finish(); - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onMediaPlayerError(PlayerErrorEvent event) { - MediaPlayerErrorDialog.show(this, event); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(MessageEvent event) { - Log.d(TAG, "onEvent(" + event + ")"); - final MaterialAlertDialogBuilder errorDialog = new MaterialAlertDialogBuilder(this); - errorDialog.setMessage(event.message); - if (event.action != null) { - errorDialog.setPositiveButton(event.actionText, (dialog, which) -> event.action.accept(this)); - } - errorDialog.show(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - requestCastButton(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.mediaplayer, menu); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - if (controller == null) { - return false; - } - Playable media = controller.getMedia(); - boolean isFeedMedia = (media instanceof FeedMedia); - - menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed - - boolean hasWebsiteLink = getWebsiteLinkWithFallback(media) != null; - menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink); - - boolean isItemAndHasLink = isFeedMedia && ShareUtils.hasLinkToShare(((FeedMedia) media).getItem()); - boolean isItemHasDownloadLink = isFeedMedia && ((FeedMedia) media).getDownloadUrl() != null; - menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink); - - menu.findItem(R.id.add_to_favorites_item).setVisible(false); - menu.findItem(R.id.remove_from_favorites_item).setVisible(false); - if (isFeedMedia) { - menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite); - menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite); - } - - menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive()); - menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive()); - - menu.findItem(R.id.player_switch_to_audio_only).setVisible(true); - - menu.findItem(R.id.audio_controls).setVisible(controller.getAudioTracks().size() >= 2); - menu.findItem(R.id.playback_speed).setVisible(true); - menu.findItem(R.id.player_show_chapters).setVisible(true); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.player_switch_to_audio_only) { - switchToAudioOnly = true; - finish(); - return true; - } else if (item.getItemId() == android.R.id.home) { - Intent intent = new Intent(VideoplayerActivity.this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - finish(); - return true; - } else if (item.getItemId() == R.id.player_show_chapters) { - new ChaptersFragment().show(getSupportFragmentManager(), ChaptersFragment.TAG); - return true; - } - - if (controller == null) { - return false; - } - - Playable media = controller.getMedia(); - if (media == null) { - return false; - } - final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem - if (item.getItemId() == R.id.add_to_favorites_item && feedItem != null) { - DBWriter.addFavoriteItem(feedItem); - isFavorite = true; - invalidateOptionsMenu(); - } else if (item.getItemId() == R.id.remove_from_favorites_item && feedItem != null) { - DBWriter.removeFavoriteItem(feedItem); - isFavorite = false; - invalidateOptionsMenu(); - } else if (item.getItemId() == R.id.disable_sleeptimer_item - || item.getItemId() == R.id.set_sleeptimer_item) { - new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog"); - } else if (item.getItemId() == R.id.audio_controls) { - PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(); - dialog.show(getSupportFragmentManager(), "playback_controls"); - } else if (item.getItemId() == R.id.open_feed_item && feedItem != null) { - Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId()); - startActivity(intent); - } else if (item.getItemId() == R.id.visit_website_item) { - IntentUtils.openInBrowser(VideoplayerActivity.this, getWebsiteLinkWithFallback(media)); - } else if (item.getItemId() == R.id.share_item && feedItem != null) { - ShareDialog shareDialog = ShareDialog.newInstance(feedItem); - shareDialog.show(getSupportFragmentManager(), "ShareEpisodeDialog"); - } else if (item.getItemId() == R.id.playback_speed) { - new VariableSpeedDialog().show(getSupportFragmentManager(), null); - } else { - return false; - } - return true; - } - - private static String getWebsiteLinkWithFallback(Playable media) { - if (media == null) { - return null; - } else if (StringUtils.isNotBlank(media.getWebsiteLink())) { - return media.getWebsiteLink(); - } else if (media instanceof FeedMedia) { - return FeedItemUtil.getLinkWithFallback(((FeedMedia) media).getItem()); - } - return null; - } - - void onPositionObserverUpdate() { - if (controller == null) { - return; - } - - TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier()); - int currentPosition = converter.convert(controller.getPosition()); - int duration = converter.convert(controller.getDuration()); - int remainingTime = converter.convert( - controller.getDuration() - controller.getPosition()); - Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition)); - if (currentPosition == Playable.INVALID_TIME - || duration == Playable.INVALID_TIME) { - Log.w(TAG, "Could not react to position observer update because of invalid time"); - return; - } - viewBinding.positionLabel.setText(Converter.getDurationStringLong(currentPosition)); - if (showTimeLeft) { - viewBinding.durationLabel.setText("-" + Converter.getDurationStringLong(remainingTime)); - } else { - viewBinding.durationLabel.setText(Converter.getDurationStringLong(duration)); - } - updateProgressbarPosition(currentPosition, duration); - } - - private void updateProgressbarPosition(int position, int duration) { - Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")"); - float progress = ((float) position) / duration; - viewBinding.sbPosition.setProgress((int) (progress * viewBinding.sbPosition.getMax())); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (controller == null) { - return; - } - if (fromUser) { - prog = progress / ((float) seekBar.getMax()); - TimeSpeedConverter converter = new TimeSpeedConverter(controller.getCurrentPlaybackSpeedMultiplier()); - int position = converter.convert((int) (prog * controller.getDuration())); - viewBinding.seekPositionLabel.setText(Converter.getDurationStringLong(position)); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - viewBinding.seekCardView.setScaleX(.8f); - viewBinding.seekCardView.setScaleY(.8f); - viewBinding.seekCardView.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(1f).scaleX(1f).scaleY(1f) - .setDuration(200) - .start(); - videoControlsHider.removeCallbacks(hideVideoControls); - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - if (controller != null) { - controller.seekTo((int) (prog * controller.getDuration())); - } - viewBinding.seekCardView.setScaleX(1f); - viewBinding.seekCardView.setScaleY(1f); - viewBinding.seekCardView.animate() - .setInterpolator(new FastOutSlowInInterpolator()) - .alpha(0f).scaleX(.8f).scaleY(.8f) - .setDuration(200) - .start(); - setupVideoControlsToggler(); - } - - private void checkFavorite() { - FeedItem feedItem = getFeedItem(controller.getMedia()); - if (feedItem == null) { - return; - } - if (disposable != null) { - disposable.dispose(); - } - disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - item -> { - boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE); - if (isFavorite != isFav) { - isFavorite = isFav; - invalidateOptionsMenu(); - } - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - @Nullable - private static FeedItem getFeedItem(@Nullable Playable playable) { - if (playable instanceof FeedMedia) { - return ((FeedMedia) playable).getItem(); - } else { - return null; - } - } - - private void compatEnterPictureInPicture() { - if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - getSupportActionBar().hide(); - hideVideoControls(false); - enterPictureInPictureMode(); - } - } - - //Hardware keyboard support - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - View currentFocus = getCurrentFocus(); - if (currentFocus instanceof EditText) { - return super.onKeyUp(keyCode, event); - } - - AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); - - switch (keyCode) { - case KeyEvent.KEYCODE_P: //Fallthrough - case KeyEvent.KEYCODE_SPACE: - onPlayPause(); - toggleVideoControlsVisibility(); - return true; - case KeyEvent.KEYCODE_J: //Fallthrough - case KeyEvent.KEYCODE_A: - case KeyEvent.KEYCODE_COMMA: - onRewind(); - showSkipAnimation(false); - return true; - case KeyEvent.KEYCODE_K: //Fallthrough - case KeyEvent.KEYCODE_D: - case KeyEvent.KEYCODE_PERIOD: - onFastForward(); - showSkipAnimation(true); - return true; - case KeyEvent.KEYCODE_F: //Fallthrough - case KeyEvent.KEYCODE_ESCAPE: - //Exit fullscreen mode - onBackPressed(); - return true; - case KeyEvent.KEYCODE_I: - compatEnterPictureInPicture(); - return true; - case KeyEvent.KEYCODE_PLUS: //Fallthrough - case KeyEvent.KEYCODE_W: - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); - return true; - case KeyEvent.KEYCODE_MINUS: //Fallthrough - case KeyEvent.KEYCODE_S: - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI); - return true; - case KeyEvent.KEYCODE_M: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_TOGGLE_MUTE, AudioManager.FLAG_SHOW_UI); - return true; - } - break; - } - - //Go to x% of video: - if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { - controller.seekTo((int) (0.1f * (keyCode - KeyEvent.KEYCODE_0) * controller.getDuration())); - return true; - } - return super.onKeyUp(keyCode, event); - } -} |