diff options
author | ByteHamster <info@bytehamster.com> | 2020-10-25 17:31:47 +0100 |
---|---|---|
committer | ByteHamster <info@bytehamster.com> | 2020-10-25 17:31:47 +0100 |
commit | c8a2f20000032228226149500767ba903752e0a5 (patch) | |
tree | 0c56db9a3d01f6ce916d26d84a7ae9ca0ec5b60d /app/src/main/java | |
parent | 28ebbedbdf34b72b31c536a118bcf5108b3ea7e5 (diff) | |
parent | 3e4e6381bd39a40b210e5b6ab054e3adee371330 (diff) | |
download | AntennaPod-c8a2f20000032228226149500767ba903752e0a5.zip |
Merge branch 'develop' into add-local-feeds
Diffstat (limited to 'app/src/main/java')
58 files changed, 1097 insertions, 321 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index ed3f4e8f1..534d48479 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -12,6 +12,8 @@ import com.joanzapata.iconify.fonts.MaterialModule; import de.danoeh.antennapod.activity.SplashActivity; import de.danoeh.antennapod.core.ApCoreEventBusIndex; import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.error.CrashReportWriter; +import de.danoeh.antennapod.error.RxJavaErrorHandlerSetup; import de.danoeh.antennapod.spa.SPAUtil; import org.greenrobot.eventbus.EventBus; @@ -38,6 +40,7 @@ public class PodcastApp extends Application { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter()); + RxJavaErrorHandlerSetup.setupRxJavaErrorHandler(); if (BuildConfig.DEBUG) { StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder() diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java index b1a0ba2a2..bff11fa5e 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java @@ -3,11 +3,22 @@ package de.danoeh.antennapod.activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +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.appcompat.app.AppCompatActivity; +import androidx.core.content.FileProvider; + + import android.widget.TextView; -import de.danoeh.antennapod.CrashReportWriter; + + +import de.danoeh.antennapod.error.CrashReportWriter; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.IntentUtils; @@ -17,11 +28,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.Charset; +import java.util.List; /** * Displays the 'crash report' screen */ public class BugReportActivity extends AppCompatActivity { + private static final String TAG = "BugReportActivity"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -30,17 +43,20 @@ public class BugReportActivity extends AppCompatActivity { getSupportActionBar().setDisplayShowHomeEnabled(true); setContentView(R.layout.bug_report); - String crashDetailsText = CrashReportWriter.getSystemInfo() + "\n\n"; - TextView crashDetailsTextView = findViewById(R.id.crash_report_logs); - + String stacktrace = "No crash report recorded"; try { File crashFile = CrashReportWriter.getFile(); - crashDetailsText += IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8")); + if (crashFile.exists()) { + stacktrace = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8")); + } else { + Log.d(TAG, stacktrace); + } } catch (IOException e) { e.printStackTrace(); - crashDetailsText += "No crash report recorded"; } - crashDetailsTextView.setText(crashDetailsText); + + 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")); @@ -58,12 +74,35 @@ public class BugReportActivity extends AppCompatActivity { filename.createNewFile(); String cmd = "logcat -d -f " + filename.getAbsolutePath(); Runtime.getRuntime().exec(cmd); - Snackbar.make(findViewById(android.R.id.content), - filename.getAbsolutePath(), Snackbar.LENGTH_SHORT).show(); + //share file + try { + Intent i = new Intent(Intent.ACTION_SEND); + i.setType("text/*"); + String authString = getString(de.danoeh.antennapod.core.R.string.provider_authority); + Uri fileUri = FileProvider.getUriForFile(this, authString, filename); + i.putExtra(Intent.EXTRA_STREAM, fileUri); + i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + PackageManager pm = getPackageManager(); + List<ResolveInfo> resInfos = pm.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY); + for (ResolveInfo resolveInfo : resInfos) { + String packageName = resolveInfo.activityInfo.packageName; + grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } + String chooserTitle = getString(de.danoeh.antennapod.core.R.string.share_file_label); + startActivity(Intent.createChooser(i, chooserTitle)); + } 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 f772cb084..6f237e1aa 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -6,12 +6,15 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; +import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; +import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -22,6 +25,8 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; +import androidx.core.view.ViewCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -33,6 +38,8 @@ import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; +import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; @@ -71,6 +78,8 @@ public class MainActivity extends CastEnabledActivity { public static final String EXTRA_FEED_ID = "fragment_feed_id"; public static final String EXTRA_OPEN_PLAYER = "open_player"; public static final String EXTRA_REFRESH_ON_START = "refresh_on_start"; + public static final String EXTRA_STARTED_FROM_SEARCH = "started_from_search"; + public static final String KEY_GENERATED_VIEW_ID = "generated_view_id"; private @Nullable DrawerLayout drawerLayout; private @Nullable ActionBarDrawerToggle drawerToggle; @@ -92,6 +101,9 @@ public class MainActivity extends CastEnabledActivity { public void onCreate(Bundle savedInstanceState) { lastTheme = UserPreferences.getNoTitleTheme(); setTheme(lastTheme); + if (savedInstanceState != null) { + ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(KEY_GENERATED_VIEW_ID, 0)); + } super.onCreate(savedInstanceState); StorageUtils.checkStorageAvailability(this); setContentView(R.layout.main); @@ -143,6 +155,25 @@ public class MainActivity extends CastEnabledActivity { sheetBehavior.setBottomSheetCallback(bottomSheetCallback); } + /** + * ViewCompat.generateViewId stores the current ID in a static variable. + * When the process is killed, the variable gets reset. + * This makes sure that we do not get ID collisions + * and therefore errors when trying to restore state from another view. + */ + @SuppressWarnings("StatementWithEmptyBody") + private void ensureGeneratedViewIdGreaterThan(int minimum) { + while (ViewCompat.generateViewId() <= minimum) { + // Generate new IDs + } + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(KEY_GENERATED_VIEW_ID, ViewCompat.generateViewId()); + } + private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override @@ -181,7 +212,7 @@ public class MainActivity extends CastEnabledActivity { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { loadFragment(AddFeedFragment.TAG, null); - new Handler().postDelayed(() -> { + new Handler(Looper.getMainLooper()).postDelayed(() -> { if (drawerLayout != null) { // Tablet layout does not have a drawer drawerLayout.openDrawer(navDrawer); } @@ -477,7 +508,11 @@ public class MainActivity extends CastEnabledActivity { if (tag != null) { loadFragment(tag, args); } else if (feedId > 0) { - loadFeedFragmentById(feedId, args); + if (intent.getBooleanExtra(EXTRA_STARTED_FROM_SEARCH, false)) { + loadChildFragment(FeedItemlistFragment.newInstance(feedId)); + } else { + loadFeedFragmentById(feedId, args); + } } sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } else if (intent.getBooleanExtra(EXTRA_OPEN_PLAYER, false)) { @@ -511,4 +546,53 @@ public class MainActivity extends CastEnabledActivity { public Snackbar showSnackbarAbovePlayer(int text, int duration) { return showSnackbarAbovePlayer(getResources().getText(text), duration); } + + //Hardware keyboard support + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + Integer customKeyCode = null; + + switch (keyCode) { + case KeyEvent.KEYCODE_P: + customKeyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; + break; + case KeyEvent.KEYCODE_J: //Fallthrough + case KeyEvent.KEYCODE_A: + case KeyEvent.KEYCODE_COMMA: + customKeyCode = KeyEvent.KEYCODE_MEDIA_REWIND; + break; + case KeyEvent.KEYCODE_K: //Fallthrough + case KeyEvent.KEYCODE_D: + case KeyEvent.KEYCODE_PERIOD: + customKeyCode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD; + break; + 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; + } + + if (customKeyCode != null) { + Intent intent = new Intent(this, PlaybackService.class); + intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, customKeyCode); + ContextCompat.startForegroundService(this, intent); + return true; + } + return super.onKeyUp(keyCode, event); + } + } 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 f6623d5dc..3bacdc75f 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -508,6 +508,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { // feed.getId() is always 0, we have to retrieve the id from the feed list from // the database Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed)); + intent.putExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, + getIntent().getBooleanExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, false)); finish(); startActivity(intent); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java index 4f6010b75..6e526911b 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -19,6 +19,7 @@ import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.NetworkPreferencesFragment; +import de.danoeh.antennapod.fragment.preferences.NotificationPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.StoragePreferencesFragment; import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragment; @@ -70,6 +71,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe prefFragment = new GpodderPreferencesFragment(); } else if (screen == R.xml.preferences_playback) { prefFragment = new PlaybackPreferencesFragment(); + } else if (screen == R.xml.preferences_notifications) { + prefFragment = new NotificationPreferencesFragment(); } return prefFragment; } @@ -90,6 +93,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe return R.string.user_interface_label; case R.xml.preferences_gpodder: return R.string.gpodnet_main_label; + case R.xml.preferences_notifications: + return R.string.notification_pref_fragment; default: return R.string.settings_label; } 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 8c66b6a4c..2d4510e8f 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -3,14 +3,18 @@ package de.danoeh.antennapod.activity; import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.drawable.ColorDrawable; +import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.view.Gravity; +import android.view.KeyEvent; import android.view.animation.AlphaAnimation; import android.view.animation.AnimationSet; import android.view.animation.ScaleAnimation; import android.widget.ImageView; + +import androidx.appcompat.view.menu.ActionMenuItem; import androidx.core.view.WindowCompat; import androidx.appcompat.app.ActionBar; import android.text.TextUtils; @@ -102,6 +106,7 @@ public class VideoplayerActivity extends MediaplayerActivity { if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { videoControlsHider.stop(); } + progressIndicator.setVisibility(View.GONE); // Controller released; we will not receive buffering updates } @Override @@ -187,7 +192,7 @@ public class VideoplayerActivity extends MediaplayerActivity { videoControlsHider.stop(); if (System.currentTimeMillis() - lastScreenTap < 300) { - if (event.getX() > v.getMeasuredWidth() / 2) { + if (event.getX() > v.getMeasuredWidth() / 2.0f) { onFastForward(); showSkipAnimation(true); } else { @@ -480,4 +485,62 @@ public class VideoplayerActivity extends MediaplayerActivity { } + + //Hardware keyboard support + @Override + public boolean onKeyUp(int keyCode, KeyEvent 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); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java index cb72a9150..002147071 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java @@ -63,7 +63,7 @@ public class ChaptersListAdapter extends RecyclerView.Adapter<ChaptersListAdapte duration = media.getDuration() - sc.getStart(); } holder.duration.setText(context.getString(R.string.chapter_duration, - Converter.getDurationStringLong((int) duration))); + Converter.getDurationStringLocalized(context, (int) duration))); if (sc.getLink() == null) { holder.link.setVisibility(View.GONE); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java index 721b720d1..3c4003125 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java @@ -4,6 +4,7 @@ import android.content.Context; import android.text.format.Formatter; import java.util.List; +import java.util.Locale; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.storage.StatisticsItem; @@ -40,7 +41,10 @@ public class DownloadStatisticsListAdapter extends StatisticsListAdapter { @Override void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem item) { - holder.value.setText(Formatter.formatShortFileSize(context, item.totalDownloadSize)); + holder.value.setText(Formatter.formatShortFileSize(context, item.totalDownloadSize) + + " • " + + String.format(Locale.getDefault(), "%d%s", + item.episodesDownloadCount, context.getString(R.string.episodes_suffix))); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index a53adc719..92ed7b052 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -298,7 +298,7 @@ public class NavListAdapter extends BaseAdapter convertView = inflater.inflate(R.layout.nav_section_item, parent, false); TextView feedsFilteredMsg = convertView.findViewById(R.id.nav_feeds_filtered_message); - if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE && showSubscriptionList) { + if (UserPreferences.getSubscriptionsFilter().isEnabled() && showSubscriptionList) { convertView.setEnabled(true); feedsFilteredMsg.setText("{md-info-outline} " + context.getString(R.string.subscriptions_are_filtered)); Iconify.addIcons(feedsFilteredMsg); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java index 46712a666..7b8659968 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java @@ -8,6 +8,7 @@ import androidx.annotation.AttrRes; import androidx.annotation.StringRes; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.util.IntentUtils; public class VisitWebsiteActionButton extends ItemActionButton { @@ -29,8 +30,7 @@ public class VisitWebsiteActionButton extends ItemActionButton { @Override public void onClick(Context context) { - Uri uri = Uri.parse(item.getLink()); - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + IntentUtils.openInBrowser(context, item.getLink()); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java deleted file mode 100644 index 3b0e2d04b..000000000 --- a/app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.danoeh.antennapod.dialog; - -import android.content.Context; - -import androidx.appcompat.app.AlertDialog; - -import org.greenrobot.eventbus.EventBus; - -import java.util.Arrays; -import java.util.List; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; -import de.danoeh.antennapod.core.preferences.UserPreferences; - -public class FeedFilterDialog { - public static void showDialog(Context context) { - AlertDialog.Builder dialog = new AlertDialog.Builder(context); - dialog.setTitle(context.getString(R.string.pref_filter_feed_title)); - dialog.setNegativeButton(android.R.string.cancel, (d, listener) -> d.dismiss()); - - int selected = UserPreferences.getFeedFilter(); - List<String> entryValues = - Arrays.asList(context.getResources().getStringArray(R.array.nav_drawer_feed_filter_values)); - final int selectedIndex = entryValues.indexOf("" + selected); - - String[] items = context.getResources().getStringArray(R.array.nav_drawer_feed_filter_options); - dialog.setSingleChoiceItems(items, selectedIndex, (d, which) -> { - if (selectedIndex != which) { - UserPreferences.setFeedFilter(entryValues.get(which)); - //Update subscriptions - EventBus.getDefault().post(new UnreadItemsUpdateEvent()); - } - d.dismiss(); - }); - dialog.show(); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java index e45533826..98f6cc117 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.dialog; import android.app.Dialog; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; @@ -94,7 +95,7 @@ public class PlaybackControlsDialog extends DialogFragment { barRightVolume.setEnabled(false); } - final CheckBox skipSilence = (CheckBox) dialog.findViewById(R.id.skipSilence); + final CheckBox skipSilence = dialog.findViewById(R.id.skipSilence); skipSilence.setChecked(UserPreferences.isSkipSilence()); if (!UserPreferences.useExoplayer()) { skipSilence.setEnabled(false); @@ -159,7 +160,7 @@ public class PlaybackControlsDialog extends DialogFragment { butAudioTracks.setText(audioTracks.get(selectedAudioTrack)); butAudioTracks.setOnClickListener(v -> { controller.setAudioTrack((selectedAudioTrack + 1) % audioTracks.size()); - new Handler().postDelayed(this::setupAudioTracks, 500); + new Handler(Looper.getMainLooper()).postDelayed(this::setupAudioTracks, 500); }); } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java deleted file mode 100644 index 7cb274708..000000000 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ /dev/null @@ -1,113 +0,0 @@ -package de.danoeh.antennapod.dialog; - -import android.app.Dialog; -import android.content.Context; -import android.content.SharedPreferences; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.concurrent.TimeUnit; - -import androidx.appcompat.app.AlertDialog; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.util.IntentUtils; - -public class RatingDialog { - - private RatingDialog(){} - - private static final String TAG = RatingDialog.class.getSimpleName(); - private static final int AFTER_DAYS = 7; - - private static WeakReference<Context> mContext; - private static SharedPreferences mPreferences; - private static Dialog mDialog; - - private static final String PREFS_NAME = "RatingPrefs"; - private static final String KEY_RATED = "KEY_WAS_RATED"; - private static final String KEY_FIRST_START_DATE = "KEY_FIRST_HIT_DATE"; - - public static void init(Context context) { - mContext = new WeakReference<>(context); - mPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); - - long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, 0); - if (firstDate == 0) { - resetStartDate(); - } - } - - public static void check() { - if (mDialog != null && mDialog.isShowing()) { - return; - } - if (shouldShow()) { - try { - mDialog = createDialog(); - if (mDialog != null) { - mDialog.show(); - } - } catch (Exception e) { - Log.e(TAG, Log.getStackTraceString(e)); - } - } - } - - private static void rateNow() { - Context context = mContext.get(); - if (context == null) { - return; - } - IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod"); - saveRated(); - } - - private static boolean rated() { - return mPreferences.getBoolean(KEY_RATED, false); - } - - @VisibleForTesting - public static void saveRated() { - mPreferences - .edit() - .putBoolean(KEY_RATED, true) - .apply(); - } - - private static void resetStartDate() { - mPreferences - .edit() - .putLong(KEY_FIRST_START_DATE, System.currentTimeMillis()) - .apply(); - } - - private static boolean shouldShow() { - if (rated()) { - return false; - } - - long now = System.currentTimeMillis(); - long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, now); - long diff = now - firstDate; - long diffDays = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); - return diffDays >= AFTER_DAYS; - } - - @Nullable - private static AlertDialog createDialog() { - Context context = mContext.get(); - if (context == null) { - return null; - } - return new AlertDialog.Builder(context) - .setTitle(R.string.rating_title) - .setMessage(R.string.rating_message) - .setPositiveButton(R.string.rating_now_label, (dialog, which) -> rateNow()) - .setNegativeButton(R.string.rating_never_label, (dialog, which) -> saveRated()) - .setNeutralButton(R.string.rating_later_label, (dialog, which) -> resetStartDate()) - .setOnCancelListener(dialog1 -> resetStartDate()) - .create(); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java index a9c54e879..274c3b7bd 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.dialog; +import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -137,6 +138,7 @@ public class SleepTimerDialog extends DialogFragment { if (controller != null) { controller.setSleepTimer(time); } + closeKeyboard(content); } catch (NumberFormatException e) { e.printStackTrace(); Snackbar.make(content, R.string.time_dialog_invalid_input, Snackbar.LENGTH_LONG).show(); @@ -153,4 +155,9 @@ public class SleepTimerDialog extends DialogFragment { timeDisplay.setVisibility(controller.sleepTimerActive() ? View.VISIBLE : View.GONE); time.setText(Converter.getDurationStringLong((int) controller.getSleepTimerTimeLeft())); } + + private void closeKeyboard(View content) { + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(content.getWindowToken(), 0); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java new file mode 100644 index 000000000..a8915480c --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java @@ -0,0 +1,82 @@ +package de.danoeh.antennapod.dialog; + +import android.content.Context; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.RadioButton; + +import androidx.appcompat.app.AlertDialog; + +import org.greenrobot.eventbus.EventBus; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.core.feed.SubscriptionsFilter; +import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.view.RecursiveRadioGroup; + +public class SubscriptionsFilterDialog { + public static void showDialog(Context context) { + SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter(); + final Set<String> filterValues = new HashSet<>(Arrays.asList(subscriptionsFilter.getValues())); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(context.getString(R.string.pref_filter_feed_title)); + + LayoutInflater inflater = LayoutInflater.from(context); + LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.filter_dialog, null, false); + builder.setView(layout); + + for (SubscriptionsFilterGroup item : SubscriptionsFilterGroup.values()) { + RecursiveRadioGroup row = (RecursiveRadioGroup) inflater.inflate(R.layout.filter_dialog_row, null); + RadioButton filter1 = row.findViewById(R.id.filter_dialog_radioButton1); + RadioButton filter2 = row.findViewById(R.id.filter_dialog_radioButton2); + filter1.setText(item.values[0].displayName); + filter1.setTag(item.values[0].filterId); + if (item.values.length == 2) { + filter2.setText(item.values[1].displayName); + filter2.setTag(item.values[1].filterId); + } else { + filter2.setVisibility(View.GONE); + } + layout.addView(row); + } + + for (String filterId : filterValues) { + if (!TextUtils.isEmpty(filterId)) { + ((RadioButton) layout.findViewWithTag(filterId)).setChecked(true); + } + } + + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + filterValues.clear(); + for (int i = 0; i < layout.getChildCount(); i++) { + if (!(layout.getChildAt(i) instanceof RecursiveRadioGroup)) { + continue; + } + RecursiveRadioGroup group = (RecursiveRadioGroup) layout.getChildAt(i); + if (group.getCheckedButton() != null) { + String tag = (String) group.getCheckedButton().getTag(); + if (tag != null) { // Clear buttons use no tag + filterValues.add((String) group.getCheckedButton().getTag()); + } + } + } + updateFilter(filterValues); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.show(); + } + + private static void updateFilter(Set<String> filterValues) { + SubscriptionsFilter subscriptionsFilter = new SubscriptionsFilter(filterValues.toArray(new String[0])); + UserPreferences.setSubscriptionsFilter(subscriptionsFilter); + EventBus.getDefault().post(new UnreadItemsUpdateEvent()); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index ef8ed335d..1fc7a77b2 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -45,9 +45,8 @@ public class VariableSpeedDialog extends DialogFragment { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.no_playback_plugin_title); builder.setMessage(R.string.no_playback_plugin_or_sonic_msg); - builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> { - UserPreferences.enableSonic(); - }); + builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> + UserPreferences.enableSonic()); builder.setNeutralButton(R.string.close_label, null); builder.show(); } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java index e93a89ef0..e1034f89b 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.discovery; import android.content.Context; +import android.content.SharedPreferences; import android.util.Log; import de.danoeh.antennapod.R; @@ -23,24 +24,46 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import static android.content.Context.MODE_PRIVATE; + public class ItunesTopListLoader { private static final String TAG = "ITunesTopListLoader"; private final Context context; + public static final String PREF_KEY_COUNTRY_CODE = "country_code"; + public static final String PREFS = "CountryRegionPrefs"; + public static final String DISCOVER_HIDE_FAKE_COUNTRY_CODE = "00"; + public static final String COUNTRY_CODE_UNSET = "99"; public ItunesTopListLoader(Context context) { this.context = context; } - public Single<List<PodcastSearchResult>> loadToplist(int limit) { + public Single<List<PodcastSearchResult>> loadToplist() { + String defaultCountry = Locale.getDefault().getCountry(); + SharedPreferences prefs = context.getSharedPreferences(PREFS, MODE_PRIVATE); + String countryCode = prefs.getString(PREF_KEY_COUNTRY_CODE, COUNTRY_CODE_UNSET); + return this.loadToplist(countryCode, 25); + } + + public Single<List<PodcastSearchResult>> loadToplist(String country, int limit) { return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) emitter -> { - String country = Locale.getDefault().getCountry(); OkHttpClient client = AntennapodHttpClient.getHttpClient(); String feedString; + String loadCountry = country; + if (COUNTRY_CODE_UNSET.equals(country)) { + loadCountry = Locale.getDefault().getCountry(); + } try { - feedString = getTopListFeed(client, country, limit); + feedString = getTopListFeed(client, loadCountry, limit); } catch (IOException e) { - feedString = getTopListFeed(client, "us", limit); + if (COUNTRY_CODE_UNSET.equals(country)) { + feedString = getTopListFeed(client, "US", limit); + } else { + emitter.onError(e); + return; + } } + List<PodcastSearchResult> podcasts = parseFeed(feedString); emitter.onSuccess(podcasts); }) @@ -59,6 +82,9 @@ public class ItunesTopListLoader { if (response.isSuccessful()) { return response.body().string(); } + if (response.code() == 400) { + throw new IOException("iTunes does not have data for the selected country."); + } String prefix = context.getString(R.string.error_msg_prefix); throw new IOException(prefix + response); } @@ -66,8 +92,14 @@ public class ItunesTopListLoader { private List<PodcastSearchResult> parseFeed(String jsonString) throws JSONException { JSONObject result = new JSONObject(jsonString); - JSONObject feed = result.getJSONObject("feed"); - JSONArray entries = feed.getJSONArray("entry"); + JSONObject feed; + JSONArray entries; + try { + feed = result.getJSONObject("feed"); + entries = feed.getJSONArray("entry"); + } catch (JSONException e) { + return new ArrayList<>(); + } List<PodcastSearchResult> results = new ArrayList<>(); for (int i = 0; i < entries.length(); i++) { diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java new file mode 100644 index 000000000..c8e5dc4ef --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java @@ -0,0 +1,126 @@ +package de.danoeh.antennapod.discovery; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import de.danoeh.antennapod.BuildConfig; +import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class PodcastIndexPodcastSearcher implements PodcastSearcher { + private static final String PODCASTINDEX_API_URL = "https://api.podcastindex.org/api/1.0/search/byterm?q=%s"; + + public PodcastIndexPodcastSearcher() { + } + + @Override + public Single<List<PodcastSearchResult>> search(String query) { + return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> { + + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.clear(); + Date now = new Date(); + calendar.setTime(now); + long secondsSinceEpoch = calendar.getTimeInMillis() / 1000L; + String apiHeaderTime = String.valueOf(secondsSinceEpoch); + String data4Hash = BuildConfig.PODCASTINDEX_API_KEY + BuildConfig.PODCASTINDEX_API_SECRET + apiHeaderTime; + String hashString = sha1(data4Hash); + + String encodedQuery; + try { + encodedQuery = URLEncoder.encode(query, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // this won't ever be thrown + encodedQuery = query; + } + + String formattedUrl = String.format(PODCASTINDEX_API_URL, encodedQuery); + + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + Request.Builder httpReq = new Request.Builder() + .addHeader("X-Auth-Date", apiHeaderTime) + .addHeader("X-Auth-Key", BuildConfig.PODCASTINDEX_API_KEY) + .addHeader("Authorization", hashString) + .addHeader("User-Agent", ClientConfig.USER_AGENT) + .url(formattedUrl); + List<PodcastSearchResult> podcasts = new ArrayList<>(); + try { + Response response = client.newCall(httpReq.build()).execute(); + + if (response.isSuccessful()) { + String resultString = response.body().string(); + JSONObject result = new JSONObject(resultString); + JSONArray j = result.getJSONArray("feeds"); + + for (int i = 0; i < j.length(); i++) { + JSONObject podcastJson = j.getJSONObject(i); + PodcastSearchResult podcast = PodcastSearchResult.fromPodcastIndex(podcastJson); + if (podcast.feedUrl != null) { + podcasts.add(podcast); + } + } + } else { + subscriber.onError(new IOException(response.toString())); + } + } catch (IOException | JSONException e) { + subscriber.onError(e); + } + subscriber.onSuccess(podcasts); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + @Override + public Single<String> lookupUrl(String url) { + return Single.just(url); + } + + @Override + public boolean urlNeedsLookup(String url) { + return false; + } + + @Override + public String getName() { + return "Podcastindex.org"; + } + + private static String sha1(String clearString) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); + messageDigest.update(clearString.getBytes("UTF-8")); + return toHex(messageDigest.digest()); + } catch (Exception ignored) { + ignored.printStackTrace(); + return null; + } + } + + private static String toHex(byte[] bytes) { + StringBuilder buffer = new StringBuilder(); + for (byte b : bytes) { + buffer.append(String.format(Locale.getDefault(), "%02x", b)); + } + return buffer.toString(); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java index 0f0c864b1..bba438d1d 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java @@ -103,4 +103,12 @@ public class PodcastSearchResult { searchHit.getUrl(), searchHit.getAuthor()); } + + public static PodcastSearchResult fromPodcastIndex(JSONObject json) { + String title = json.optString("title", ""); + String imageUrl = json.optString("image", null); + String feedUrl = json.optString("url", null); + String author = json.optString("author", null); + return new PodcastSearchResult(title, imageUrl, feedUrl, author); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java index 3f738424b..ad574cab6 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java @@ -19,6 +19,7 @@ public class PodcastSearcherRegistry { searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.f)); searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.f)); searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f)); + searchProviders.add(new SearcherInfo(new PodcastIndexPodcastSearcher(), 0.0f)); } return searchProviders; } diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java index b5d0f6945..dc62863f9 100644 --- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java +++ b/app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java @@ -1,8 +1,9 @@ -package de.danoeh.antennapod; +package de.danoeh.antennapod.error; import android.os.Build; import android.util.Log; +import de.danoeh.antennapod.BuildConfig; import org.apache.commons.io.IOUtils; import java.io.File; @@ -31,6 +32,11 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread thread, Throwable ex) { + write(ex); + defaultHandler.uncaughtException(thread, ex); + } + + public static void write(Throwable exception) { File path = getFile(); PrintWriter out = null; try { @@ -41,14 +47,13 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler { out.println(); out.println("## StackTrace"); out.println("```"); - ex.printStackTrace(out); + exception.printStackTrace(out); out.println("```"); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); } finally { IOUtils.closeQuietly(out); } - defaultHandler.uncaughtException(thread, ex); } public static String getSystemInfo() { diff --git a/app/src/main/java/de/danoeh/antennapod/error/RxJavaErrorHandlerSetup.java b/app/src/main/java/de/danoeh/antennapod/error/RxJavaErrorHandlerSetup.java new file mode 100644 index 000000000..1c7f5f0b4 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/error/RxJavaErrorHandlerSetup.java @@ -0,0 +1,36 @@ +package de.danoeh.antennapod.error; + +import android.util.Log; +import de.danoeh.antennapod.BuildConfig; +import io.reactivex.exceptions.UndeliverableException; +import io.reactivex.plugins.RxJavaPlugins; + +public class RxJavaErrorHandlerSetup { + private static final String TAG = "RxJavaErrorHandler"; + + private RxJavaErrorHandlerSetup() { + + } + + public static void setupRxJavaErrorHandler() { + RxJavaPlugins.setErrorHandler(exception -> { + if (exception instanceof UndeliverableException) { + // Probably just disposed because the fragment was left + Log.d(TAG, "Ignored exception: " + Log.getStackTraceString(exception)); + return; + } + + // Usually, undeliverable exceptions are wrapped in an UndeliverableException. + // If an undeliverable exception is a NPE (or some others), wrapping does not happen. + // AntennaPod threads might throw NPEs after disposing because we set controllers to null. + // Just swallow all exceptions here. + Log.e(TAG, Log.getStackTraceString(exception)); + CrashReportWriter.write(exception); + + if (BuildConfig.DEBUG) { + Thread.currentThread().getUncaughtExceptionHandler() + .uncaughtException(Thread.currentThread(), exception); + } + }); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index 5e014db3f..fa0df9abb 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -33,6 +33,7 @@ import de.danoeh.antennapod.core.util.SortOrder; import de.danoeh.antennapod.discovery.CombinedSearcher; import de.danoeh.antennapod.discovery.FyydPodcastSearcher; import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; +import de.danoeh.antennapod.discovery.PodcastIndexPodcastSearcher; import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment; import java.util.Collections; @@ -65,6 +66,8 @@ public class AddFeedFragment extends Fragment { -> activity.loadChildFragment(OnlineSearchFragment.newInstance(FyydPodcastSearcher.class))); root.findViewById(R.id.btn_search_gpodder).setOnClickListener(v -> activity.loadChildFragment(new GpodnetMainFragment())); + root.findViewById(R.id.btn_search_podcastindex).setOnClickListener(v + -> activity.loadChildFragment(OnlineSearchFragment.newInstance(PodcastIndexPodcastSearcher.class))); combinedFeedSearchBox = root.findViewById(R.id.combinedFeedSearchBox); combinedFeedSearchBox.setOnEditorActionListener((v, actionId, event) -> { 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 4a1c12e0a..0c75b7eea 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -89,6 +89,18 @@ public class AllEpisodesFragment extends EpisodesListFragment { filterDialog.openDialog(); } + @Override + protected boolean shouldUpdatedItemRemainInList(FeedItem item) { + SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + FeedItemFilter feedItemFilter = new FeedItemFilter(prefs.getString(PREF_FILTER, "")); + + if (feedItemFilter.isShowDownloaded() && (!item.hasMedia() || !item.getMedia().isDownloaded())) { + return false; + } + + return true; + } + @NonNull @Override protected List<FeedItem> loadData() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java index 7eb749681..3129aa43c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -124,8 +124,6 @@ public class AudioPlayerFragment extends Fragment implements setupLengthTextView(); setupControlButtons(); setupPlaybackSpeedButton(); - txtvRev.setText(NumberFormat.getInstance().format(UserPreferences.getRewindSecs())); - txtvFF.setText(NumberFormat.getInstance().format(UserPreferences.getFastForwardSecs())); sbPosition.setOnSeekBarChangeListener(this); pager = root.findViewById(R.id.pager); @@ -376,6 +374,8 @@ public class AudioPlayerFragment extends Fragment implements controller.init(); loadMediaInfo(); EventBus.getDefault().register(this); + txtvRev.setText(NumberFormat.getInstance().format(UserPreferences.getRewindSecs())); + txtvFF.setText(NumberFormat.getInstance().format(UserPreferences.getFastForwardSecs())); } @Override @@ -383,6 +383,7 @@ public class AudioPlayerFragment extends Fragment implements super.onStop(); controller.release(); controller = null; + progressIndicator.setVisibility(View.GONE); // Controller released; we will not receive buffering updates EventBus.getDefault().unregister(this); if (disposable != null) { disposable.dispose(); 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 79f378249..648fc614a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -45,6 +45,7 @@ import org.greenrobot.eventbus.ThreadMode; public class CoverFragment extends Fragment { private static final String TAG = "CoverFragment"; + static final double SIXTEEN_BY_NINE = 1.7; private View root; private TextView txtvPodcastTitle; @@ -188,20 +189,31 @@ public class CoverFragment extends Fragment { private void configureForOrientation(Configuration newConfig) { LinearLayout mainContainer = getView().findViewById(R.id.cover_fragment); - ViewGroup.LayoutParams params = imgvCover.getLayoutParams(); + LinearLayout textContainer = getView().findViewById(R.id.cover_fragment_text_container); + + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) imgvCover.getLayoutParams(); + LinearLayout.LayoutParams textParams = (LinearLayout.LayoutParams) textContainer.getLayoutParams(); + double ratio = (float) newConfig.screenHeightDp / (float) newConfig.screenWidthDp; if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + double percentageWidth = 0.8; + if (ratio <= SIXTEEN_BY_NINE) { + percentageWidth = (ratio / SIXTEEN_BY_NINE) * percentageWidth * 0.8; + } mainContainer.setOrientation(LinearLayout.VERTICAL); if (newConfig.screenWidthDp > 0) { - params.width = (int) (convertDpToPixel(newConfig.screenWidthDp) * .80); + params.width = (int) (convertDpToPixel(newConfig.screenWidthDp) * percentageWidth); params.height = params.width; + textParams.weight = 0; imgvCover.setLayoutParams(params); } } else { + double percentageHeight = ratio * 0.8; mainContainer.setOrientation(LinearLayout.HORIZONTAL); if (newConfig.screenHeightDp > 0) { - params.height = (int) (convertDpToPixel(newConfig.screenHeightDp) * .40); + params.height = (int) (convertDpToPixel(newConfig.screenHeightDp) * percentageHeight); params.width = params.height; + textParams.weight = 1; imgvCover.setLayoutParams(params); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java index 89d3c7af9..5bc950d50 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -1,27 +1,41 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.GridView; import android.widget.ProgressBar; +import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; + +import org.greenrobot.eventbus.EventBus; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; +import de.danoeh.antennapod.core.event.DiscoveryDefaultUpdateEvent; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Locale; + +import static android.content.Context.MODE_PRIVATE; /** * Searches iTunes store for top podcasts and displays results in a list. @@ -29,6 +43,7 @@ import java.util.List; public class DiscoveryFragment extends Fragment { private static final String TAG = "ItunesSearchFragment"; + private SharedPreferences prefs; /** * Adapter responsible with the search results. @@ -46,6 +61,7 @@ public class DiscoveryFragment extends Fragment { private List<PodcastSearchResult> searchResults; private List<PodcastSearchResult> topList; private Disposable disposable; + private String countryCode = "US"; /** * Replace adapter data with provided search results from SearchTask. @@ -75,6 +91,8 @@ public class DiscoveryFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); + prefs = getActivity().getSharedPreferences(ItunesTopListLoader.PREFS, MODE_PRIVATE); + countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().getCountry()); } @Override @@ -97,13 +115,64 @@ public class DiscoveryFragment extends Fragment { intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); startActivity(intent); }); + + List<String> countryCodeArray = new ArrayList<String>(Arrays.asList(Locale.getISOCountries())); + HashMap<String, String> countryCodeNames = new HashMap<String, String>(); + for (String code: countryCodeArray) { + Locale locale = new Locale("", code); + String countryName = locale.getDisplayCountry(); + if (countryName != null) { + countryCodeNames.put(code, countryName); + } + } + + List<String> countryNamesSort = new ArrayList<String>(countryCodeNames.values()); + Collections.sort(countryNamesSort); + countryNamesSort.add(0, getResources().getString(R.string.discover_hide)); + + Spinner countrySpinner = root.findViewById(R.id.spinner_country); + ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this.getContext(), + android.R.layout.simple_spinner_item, + countryNamesSort); + dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + countrySpinner.setAdapter(dataAdapter); + int pos = countryNamesSort.indexOf(countryCodeNames.get(countryCode)); + countrySpinner.setSelection(pos); + + countrySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> countrySpinner, View view, int position, long id) { + String countryName = (String) countrySpinner.getItemAtPosition(position); + + if (countryName.equals(getResources().getString(R.string.discover_hide))) { + countryCode = ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE; + } else { + for (Object o : countryCodeNames.keySet()) { + if (countryCodeNames.get(o).equals(countryName)) { + countryCode = o.toString(); + break; + } + } + } + + prefs.edit() + .putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode) + .apply(); + + EventBus.getDefault().post(new DiscoveryDefaultUpdateEvent()); + loadToplist(countryCode); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); progressBar = root.findViewById(R.id.progressBar); txtvError = root.findViewById(R.id.txtvError); butRetry = root.findViewById(R.id.butRetry); txtvEmpty = root.findViewById(android.R.id.empty); - loadToplist(); - + loadToplist(countryCode); return root; } @@ -116,28 +185,39 @@ public class DiscoveryFragment extends Fragment { adapter = null; } - private void loadToplist() { + private void loadToplist(String country) { if (disposable != null) { disposable.dispose(); } + gridView.setVisibility(View.GONE); txtvError.setVisibility(View.GONE); butRetry.setVisibility(View.GONE); txtvEmpty.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); - ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(25).subscribe(podcasts -> { + if (country.equals(ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE)) { + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.VISIBLE); + txtvError.setText(getResources().getString(R.string.discover_is_hidden)); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); progressBar.setVisibility(View.GONE); - topList = podcasts; - updateData(topList); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - txtvError.setText(error.toString()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> loadToplist()); - butRetry.setVisibility(View.VISIBLE); - }); + } else { + ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); + disposable = loader.loadToplist(country, 25).subscribe( + podcasts -> { + progressBar.setVisibility(View.GONE); + topList = podcasts; + updateData(topList); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.getMessage()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> loadToplist(country)); + butRetry.setVisibility(View.VISIBLE); + }); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java index c173bf8ee..bc2d85452 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -51,6 +51,7 @@ public class DownloadsFragment extends Fragment { viewPager = root.findViewById(R.id.viewpager); viewPager.setAdapter(new DownloadsPagerAdapter(this)); + viewPager.setOffscreenPageLimit(2); // Give the TabLayout the ViewPager tabLayout = root.findViewById(R.id.sliding_tabs); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index e98890627..afd027b3a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -51,6 +51,7 @@ public class EpisodesFragment extends Fragment { ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); viewPager = rootView.findViewById(R.id.viewpager); viewPager.setAdapter(new EpisodesPagerAdapter(this)); + viewPager.setOffscreenPageLimit(2); // Give the TabLayout the ViewPager tabLayout = rootView.findViewById(R.id.sliding_tabs); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 44f6c8827..62400d81d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -6,6 +6,10 @@ import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -126,7 +130,7 @@ public abstract class EpisodesListFragment extends Fragment { } super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.episodes, menu); - MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), 0); + MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), 0, ""); isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } @@ -214,6 +218,13 @@ public abstract class EpisodesListFragment extends Fragment { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); } + SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh); + swipeRefreshLayout.setOnRefreshListener(() -> { + AutoUpdateManager.runImmediate(requireContext()); + new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false), + getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms)); + }); + progLoading = root.findViewById(R.id.progLoading); progLoading.setVisibility(View.VISIBLE); loadingMoreView = root.findViewById(R.id.loadingMore); 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 94c9bd056..5d701472f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -21,6 +21,7 @@ import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.feed.util.ImageResourceUtils; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.service.playback.PlayerStatus; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import io.reactivex.Maybe; @@ -79,8 +80,16 @@ public class ExternalPlayerFragment extends Fragment { super.onActivityCreated(savedInstanceState); butPlay.setOnClickListener(v -> { if (controller != null) { - controller.playPause(); + if (controller.getMedia().getMediaType() == MediaType.VIDEO + && controller.getStatus() != PlayerStatus.PLAYING) { + controller.playPause(); + getContext().startActivity(PlaybackService + .getPlayerActivityIntent(getContext(), controller.getMedia())); + } else { + controller.playPause(); + } } + }); loadMediaInfo(); } @@ -200,7 +209,6 @@ public class ExternalPlayerFragment extends Fragment { .into(imgvCover); if (controller != null && controller.isPlayingVideoLocally()) { - butPlay.setVisibility(View.GONE); ((MainActivity) getActivity()).getBottomSheet().setLocked(true); ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java index 7b66a189f..b66f15c7e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -6,6 +6,8 @@ import android.content.res.Configuration; import android.content.Intent; import android.graphics.LightingColorFilter; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -26,6 +28,8 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.google.android.material.appbar.AppBarLayout; @@ -205,6 +209,18 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem }); EventBus.getDefault().register(this); + + SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh); + swipeRefreshLayout.setOnRefreshListener(() -> { + try { + DBTasks.forceRefreshFeed(requireContext(), feed, true); + } catch (DownloadRequestException e) { + e.printStackTrace(); + } + new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false), + getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms)); + }); + loadItems(); return root; } @@ -236,7 +252,11 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem optionsMenu = menu; FeedMenuHandler.onCreateOptionsMenu(inflater, menu); iconTintManager.updateTint(); - MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), feedID); + if (feed != null) { + MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), feedID, feed.getTitle()); + } else { + MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), feedID, ""); + } if (feed == null || feed.getLink() == null) { menu.findItem(R.id.share_link_item).setVisible(false); menu.findItem(R.id.visit_website_item).setVisible(false); @@ -261,7 +281,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (!super.onOptionsItemSelected(item)) { + if (item.getItemId() == R.id.action_search) { + item.getActionView().post(() -> iconTintManager.updateTint()); + } + if (super.onOptionsItemSelected(item)) { + return true; + } else { if (feed == null) { ((MainActivity) getActivity()).showSnackbarAbovePlayer( R.string.please_wait_for_data, Toast.LENGTH_LONG); @@ -320,8 +345,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); return true; } - } else { - return true; } } 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 ed8697adb..fc3052e20 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -11,6 +11,7 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.Timeline; import de.danoeh.antennapod.view.ShownotesWebView; @@ -82,8 +83,10 @@ public class ItemDescriptionFragment extends Fragment { if (webViewLoader != null) { webViewLoader.dispose(); } - webViewLoader = Maybe.fromCallable(this::loadData) - .subscribeOn(Schedulers.io()) + webViewLoader = Maybe.<String>create(emitter -> { + Timeline timeline = new Timeline(getActivity(), controller.getMedia()); + emitter.onSuccess(timeline.processShownotes()); + }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { webvDescription.loadDataWithBaseURL("https://127.0.0.1", data, "text/html", @@ -92,15 +95,6 @@ public class ItemDescriptionFragment extends Fragment { }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - @Nullable - private String loadData() { - if (controller == null || controller.getMedia() == null) { - return null; - } - Timeline timeline = new Timeline(getActivity(), controller.getMedia()); - return timeline.processShownotes(); - } - @Override public void onPause() { super.onPause(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java index 3b1171df4..2425a174e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -38,6 +38,8 @@ import io.reactivex.schedulers.Schedulers; public class ItemPagerFragment extends Fragment { private static final String ARG_FEEDITEMS = "feeditems"; private static final String ARG_FEEDITEM_POS = "feeditem_pos"; + private static final String KEY_PAGER_ID = "pager_id"; + private ViewPager2 pager; /** * Creates a new instance of an ItemPagerFragment. @@ -77,12 +79,16 @@ public class ItemPagerFragment extends Fragment { feedItems = getArguments().getLongArray(ARG_FEEDITEMS); int feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS); - ViewPager2 pager = layout.findViewById(R.id.pager); + pager = layout.findViewById(R.id.pager); // FragmentStatePagerAdapter documentation: // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set. // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc, // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages. int newId = ViewCompat.generateViewId(); + if (savedInstanceState != null && savedInstanceState.getInt(KEY_PAGER_ID, 0) != 0) { + // Restore state by using the same ID as before. ID collisions are prevented in MainActivity. + newId = savedInstanceState.getInt(KEY_PAGER_ID, 0); + } pager.setId(newId); pager.setAdapter(new ItemPagerAdapter(this)); pager.setCurrentItem(feedItemPos, false); @@ -100,6 +106,12 @@ public class ItemPagerFragment extends Fragment { } @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(KEY_PAGER_ID, pager.getId()); + } + + @Override public void onDestroyView() { super.onDestroyView(); EventBus.getDefault().unregister(this); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java index 8746793be..4f99e8130 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java @@ -14,6 +14,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.ProgressBar; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -37,7 +39,7 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.dialog.FeedFilterDialog; +import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -46,6 +48,7 @@ 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.util.List; @@ -72,6 +75,7 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli private int position = -1; private NavListAdapter navAdapter; private Disposable disposable; + private ProgressBar progressBar; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @@ -79,6 +83,7 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.nav_list, container, false); + progressBar = root.findViewById(R.id.progressBar); ListView navList = root.findViewById(R.id.nav_list); navAdapter = new NavListAdapter(itemAccess, getActivity()); navList.setAdapter(navAdapter); @@ -234,18 +239,18 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli startActivity(intent); } - @Subscribe + @Subscribe(threadMode = ThreadMode.MAIN) public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { loadData(); } - @Subscribe + @Subscribe(threadMode = ThreadMode.MAIN) public void onFeedListChanged(FeedListUpdateEvent event) { loadData(); } - @Subscribe + @Subscribe(threadMode = ThreadMode.MAIN) public void onQueueChanged(QueueEvent event) { Log.d(TAG, "onQueueChanged(" + event + ")"); // we are only interested in the number of queue items, not download status or position @@ -356,14 +361,20 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli }; private void loadData() { + progressBar.setVisibility(View.VISIBLE); disposable = Observable.fromCallable(DBReader::getNavDrawerData) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - navDrawerData = result; - updateSelection(); // Selected item might be a feed - navAdapter.notifyDataSetChanged(); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); + .subscribe( + result -> { + navDrawerData = result; + updateSelection(); // Selected item might be a feed + navAdapter.notifyDataSetChanged(); + progressBar.setVisibility(View.GONE); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + }); } @Override @@ -390,9 +401,9 @@ public class NavDrawerFragment extends Fragment implements AdapterView.OnItemCli startActivity(intent); } } - } else if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE + } else if (UserPreferences.getSubscriptionsFilter().isEnabled() && navAdapter.showSubscriptionList) { - FeedFilterDialog.showDialog(requireContext()); + SubscriptionsFilterDialog.showDialog(requireContext()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java index e8bd5a1e1..435590a0c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java @@ -18,6 +18,7 @@ import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.discovery.PodcastSearchResult; @@ -28,6 +29,8 @@ import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; +import static android.view.View.INVISIBLE; + public class OnlineSearchFragment extends Fragment { private static final String TAG = "FyydSearchFragment"; @@ -92,6 +95,7 @@ public class OnlineSearchFragment extends Fragment { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); ((AppCompatActivity) getActivity()).setSupportActionBar(root.findViewById(R.id.toolbar)); + root.findViewById(R.id.spinner_country).setVisibility(INVISIBLE); gridView = root.findViewById(R.id.gridView); adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); gridView.setAdapter(adapter); @@ -101,6 +105,7 @@ public class OnlineSearchFragment extends Fragment { PodcastSearchResult podcast = searchResults.get(position); Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); + intent.putExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, true); startActivity(intent); }); progressBar = root.findViewById(R.id.progressBar); 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 26216b375..122524b48 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -246,7 +246,7 @@ public class QueueFragment extends Fragment { super.onCreateOptionsMenu(menu, inflater); if (queue != null) { inflater.inflate(R.menu.queue, menu); - MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), 0); + MenuItemUtils.setupSearchItem(menu, (MainActivity) getActivity(), 0, ""); MenuItemUtils.refreshLockItem(getActivity(), menu); // Show Lock Item only if queue is sorted manually @@ -335,10 +335,8 @@ public class QueueFragment extends Fragment { if (keepSortedNew) { SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder(); DBWriter.reorderQueue(sortOrder, true); - if (recyclerAdapter != null) { - recyclerAdapter.updateDragDropEnabled(); - } - } else if (recyclerAdapter != null) { + } + if (recyclerAdapter != null) { recyclerAdapter.updateDragDropEnabled(); } getActivity().invalidateOptionsMenu(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index d6bcdd79c..c994b4d8b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -1,27 +1,40 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.util.DisplayMetrics; import androidx.fragment.app.Fragment; + import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.Button; import android.widget.GridView; +import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.FeedDiscoverAdapter; +import de.danoeh.antennapod.core.event.DiscoveryDefaultUpdateEvent; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; +import java.util.Locale; + +import static android.content.Context.MODE_PRIVATE; public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.OnItemClickListener { @@ -33,6 +46,8 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. private FeedDiscoverAdapter adapter; private GridView discoverGridLayout; private TextView errorTextView; + private LinearLayout errorView; + private Button errorRetry; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -44,7 +59,10 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. discoverGridLayout = root.findViewById(R.id.discover_grid); progressBar = root.findViewById(R.id.discover_progress_bar); - errorTextView = root.findViewById(R.id.discover_error); + errorView = root.findViewById(R.id.discover_error); + errorTextView = root.findViewById(R.id.discover_error_txtV); + errorRetry = root.findViewById(R.id.discover_error_retry_btn); + errorRetry.setOnClickListener((listener) -> loadToplist()); adapter = new FeedDiscoverAdapter((MainActivity) getActivity()); discoverGridLayout.setAdapter(adapter); @@ -67,36 +85,67 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. adapter.updateData(dummies); loadToplist(); + + EventBus.getDefault().register(this); return root; } @Override public void onDestroy() { super.onDestroy(); + EventBus.getDefault().unregister(this); if (disposable != null) { disposable.dispose(); } } + @Subscribe(threadMode = ThreadMode.MAIN) + @SuppressWarnings("unused") + public void onDiscoveryDefaultUpdateEvent(DiscoveryDefaultUpdateEvent event) { + loadToplist(); + } + private void loadToplist() { progressBar.setVisibility(View.VISIBLE); discoverGridLayout.setVisibility(View.INVISIBLE); - errorTextView.setVisibility(View.GONE); + errorView.setVisibility(View.GONE); + errorRetry.setVisibility(View.INVISIBLE); ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(NUM_SUGGESTIONS) - .subscribe(podcasts -> { - errorTextView.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); - discoverGridLayout.setVisibility(View.VISIBLE); - adapter.updateData(podcasts); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - errorTextView.setText(error.getLocalizedMessage()); - errorTextView.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); - discoverGridLayout.setVisibility(View.INVISIBLE); - }); + SharedPreferences prefs = getActivity().getSharedPreferences(ItunesTopListLoader.PREFS, MODE_PRIVATE); + String countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, + Locale.getDefault().getCountry()); + if (countryCode.equals(ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE)) { + errorTextView.setText(String.format(getResources().getString(R.string.discover_is_hidden), + getResources().getString(R.string.discover_hide))); + errorView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.INVISIBLE); + return; + } + + disposable = loader.loadToplist(countryCode, NUM_SUGGESTIONS) + .subscribe( + podcasts -> { + errorView.setVisibility(View.GONE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.VISIBLE); + if (podcasts.size() == 0) { + errorTextView.setText(getResources().getText(R.string.search_status_no_results)); + errorView.setVisibility(View.VISIBLE); + discoverGridLayout.setVisibility(View.INVISIBLE); + } else { + adapter.updateData(podcasts); + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + errorTextView.setText(error.getLocalizedMessage()); + errorView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.VISIBLE); + }); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 0c03d407e..2061a8ba4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -17,6 +17,9 @@ import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.chip.Chip; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; @@ -43,6 +46,7 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.Collections; import java.util.List; /** @@ -52,6 +56,7 @@ public class SearchFragment extends Fragment { private static final String TAG = "SearchFragment"; private static final String ARG_QUERY = "query"; private static final String ARG_FEED = "feed"; + private static final String ARG_FEED_NAME = "feedName"; private EpisodeItemListAdapter adapter; private FeedSearchResultAdapter adapterFeeds; @@ -60,6 +65,7 @@ public class SearchFragment extends Fragment { private EmptyViewHandler emptyViewHandler; private EpisodeItemListRecyclerView recyclerView; private List<FeedItem> results; + private Chip chip; /** * Create a new SearchFragment that searches all feeds. @@ -79,9 +85,10 @@ public class SearchFragment extends Fragment { /** * Create a new SearchFragment that searches one specific feed. */ - public static SearchFragment newInstance(String query, long feed) { + public static SearchFragment newInstance(String query, long feed, String feedTitle) { SearchFragment fragment = newInstance(query); fragment.getArguments().putLong(ARG_FEED, feed); + fragment.getArguments().putString(ARG_FEED_NAME, feedTitle); return fragment; } @@ -132,6 +139,12 @@ public class SearchFragment extends Fragment { emptyViewHandler.setIcon(R.attr.action_search); emptyViewHandler.setTitle(R.string.search_status_no_results); EventBus.getDefault().register(this); + + chip = layout.findViewById(R.id.feed_title_chip); + chip.setOnCloseIconClickListener(v -> { + getArguments().putLong(ARG_FEED, 0); + search(); + }); return layout; } @@ -259,7 +272,13 @@ public class SearchFragment extends Fragment { progressBar.setVisibility(View.GONE); this.results = results.first; adapter.updateItems(results.first); - adapterFeeds.updateData(results.second); + if (getArguments().getLong(ARG_FEED, 0) == 0) { + adapterFeeds.updateData(results.second); + chip.setVisibility(View.GONE); + } else { + adapterFeeds.updateData(Collections.emptyList()); + chip.setText(getArguments().getString(ARG_FEED_NAME, "")); + } String query = getArguments().getString(ARG_QUERY); emptyViewHandler.setMessage(getString(R.string.no_results_for_query, query)); }, error -> Log.e(TAG, Log.getStackTraceString(error))); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 26145c064..3b2a72210 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -5,10 +5,14 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.widget.ProgressBar; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -45,7 +49,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; -import de.danoeh.antennapod.dialog.FeedFilterDialog; +import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.dialog.FeedSortDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.menuhandler.MenuItemUtils; @@ -102,7 +106,14 @@ public class SubscriptionFragment extends Fragment { progressBar = root.findViewById(R.id.progLoading); feedsFilteredMsg = root.findViewById(R.id.feeds_filtered_message); - feedsFilteredMsg.setOnClickListener((l) -> FeedFilterDialog.showDialog(requireContext())); + feedsFilteredMsg.setOnClickListener((l) -> SubscriptionsFilterDialog.showDialog(requireContext())); + + SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh); + swipeRefreshLayout.setOnRefreshListener(() -> { + AutoUpdateManager.runImmediate(requireContext()); + new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false), + getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms)); + }); return root; } @@ -130,7 +141,7 @@ public class SubscriptionFragment extends Fragment { AutoUpdateManager.runImmediate(requireContext()); return true; case R.id.subscriptions_filter: - FeedFilterDialog.showDialog(requireContext()); + SubscriptionsFilterDialog.showDialog(requireContext()); return true; case R.id.subscriptions_sort: FeedSortDialog.showDialog(requireContext()); @@ -214,7 +225,7 @@ public class SubscriptionFragment extends Fragment { progressBar.setVisibility(View.GONE); }, error -> Log.e(TAG, Log.getStackTraceString(error))); - if (UserPreferences.getFeedFilter() != UserPreferences.FEED_FILTER_NONE) { + if (UserPreferences.getSubscriptionsFilter().isEnabled()) { feedsFilteredMsg.setText("{md-info-outline} " + getString(R.string.subscriptions_are_filtered)); Iconify.addIcons(feedsFilteredMsg); feedsFilteredMsg.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index d5cac07f1..4cb50e2f4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -104,6 +104,7 @@ public abstract class PodcastListFragment extends Fragment { Log.d(TAG, "Selected podcast: " + selection.toString()); Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, selection.getUrl()); + intent.putExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, true); startActivity(intent); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java index 064c4b3bc..0d6e79e84 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java @@ -168,7 +168,7 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { private void buildEpisodeCleanupPreference() { final Resources res = getActivity().getResources(); - ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_EPISODE_CLEANUP); + ListPreference pref = findPreference(UserPreferences.PREF_EPISODE_CLEANUP); String[] values = res.getStringArray( R.array.episode_cleanup_values); String[] entries = new String[values.length]; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java index 546e12e65..bec73894c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java @@ -30,7 +30,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat { private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync"; private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout"; private static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname"; - private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications"; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -112,7 +111,6 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat { findPreference(PREF_GPODNET_SYNC).setEnabled(loggedIn); findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn); findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn); - findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn); if (loggedIn) { String format = getActivity().getString(R.string.pref_gpodnet_login_status); String summary = String.format(format, GpodnetPreferences.getUsername(), diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java index 5eb4a3aea..106b9eef6 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java @@ -207,11 +207,12 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat { } private void showDatabaseImportSuccessDialog() { - AlertDialog.Builder d = new AlertDialog.Builder(getContext()); - d.setMessage(R.string.import_ok); - d.setCancelable(false); - d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> PodcastApp.forceRestart()); - d.show(); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.successful_import_label); + builder.setMessage(R.string.import_ok); + builder.setCancelable(false); + builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> PodcastApp.forceRestart()); + builder.show(); } private void showExportSuccessDialog(final String path, final Uri streamUri) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java index 3f1cec729..99fd12021 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java @@ -10,6 +10,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.BugReportActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.fragment.preferences.about.AboutFragment; public class MainPreferencesFragment extends PreferenceFragmentCompat { private static final String TAG = "MainPreferencesFragment"; @@ -25,6 +26,7 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { private static final String PREF_CATEGORY_PROJECT = "project"; private static final String STATISTICS = "statistics"; private static final String PREF_ABOUT = "prefAbout"; + private static final String PREF_NOTIFICATION = "notifications"; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -67,6 +69,10 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_storage); return true; }); + findPreference(PREF_NOTIFICATION).setOnPreferenceClickListener(preference -> { + ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_notifications); + return true; + }); findPreference(PREF_ABOUT).setOnPreferenceClickListener( preference -> { @@ -120,5 +126,7 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_autodownload)); config.index(R.xml.preferences_gpodder) .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder)); + config.index(R.xml.preferences_notifications) + .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_notifications)); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java new file mode 100644 index 000000000..94e151f7a --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java @@ -0,0 +1,30 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.os.Bundle; +import androidx.preference.PreferenceFragmentCompat; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.core.preferences.GpodnetPreferences; + +public class NotificationPreferencesFragment extends PreferenceFragmentCompat { + + private static final String TAG = "NotificationPrefFragment"; + private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications"; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_notifications); + setUpScreen(); + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.notification_pref_fragment); + } + + private void setUpScreen() { + final boolean loggedIn = GpodnetPreferences.loggedIn(); + findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index f07e59afe..1fa1fed58 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -109,7 +109,7 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { private void buildSmartMarkAsPlayedPreference() { final Resources res = getActivity().getResources(); - ListPreference pref = (ListPreference) findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS); + ListPreference pref = findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS); String[] values = res.getStringArray(R.array.smart_mark_as_played_values); String[] entries = new String[values.length]; for (int x = 0; x < values.length; x++) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java index 12be76ba7..689a72ba7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.fragment.preferences; import android.content.Context; -import android.content.Intent; import android.os.Build; import android.os.Bundle; import com.google.android.material.snackbar.Snackbar; @@ -9,10 +8,9 @@ import androidx.appcompat.app.AlertDialog; import androidx.preference.PreferenceFragmentCompat; import android.widget.ListView; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.dialog.FeedFilterDialog; +import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.dialog.FeedSortDialog; import de.danoeh.antennapod.fragment.NavDrawerFragment; import org.apache.commons.lang3.ArrayUtils; @@ -79,7 +77,7 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat { findPreference(UserPreferences.PREF_FILTER_FEED) .setOnPreferenceClickListener((preference -> { - FeedFilterDialog.showDialog(requireContext()); + SubscriptionsFilterDialog.showDialog(requireContext()); return true; })); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java index eb57972a1..b440d053b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.fragment.preferences; +package de.danoeh.antennapod.fragment.preferences.about; import android.content.ClipData; import android.content.ClipboardManager; @@ -27,14 +27,9 @@ public class AboutFragment extends PreferenceFragmentCompat { Snackbar.make(getView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show(); return true; }); - findPreference("about_developers").setOnPreferenceClickListener((preference) -> { - getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment()) - .addToBackStack(getString(R.string.developers)).commit(); - return true; - }); - findPreference("about_translators").setOnPreferenceClickListener((preference) -> { - getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment()) - .addToBackStack(getString(R.string.translators)).commit(); + findPreference("about_contributors").setOnPreferenceClickListener((preference) -> { + getParentFragmentManager().beginTransaction().replace(R.id.content, new ContributorsPagerFragment()) + .addToBackStack(getString(R.string.contributors)).commit(); return true; }); findPreference("about_privacy_policy").setOnPreferenceClickListener((preference) -> { @@ -42,7 +37,7 @@ public class AboutFragment extends PreferenceFragmentCompat { return true; }); findPreference("about_licenses").setOnPreferenceClickListener((preference) -> { - getParentFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment()) + getParentFragmentManager().beginTransaction().replace(R.id.content, new LicensesFragment()) .addToBackStack(getString(R.string.translators)).commit(); return true; }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java new file mode 100644 index 000000000..20cef1313 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java @@ -0,0 +1,95 @@ +package de.danoeh.antennapod.fragment.preferences.about; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; +import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; + +/** + * Displays the 'about->Contributors' pager screen. + */ +public class ContributorsPagerFragment extends Fragment { + + public static final String TAG = "StatisticsFragment"; + + private static final int POS_DEVELOPERS = 0; + private static final int POS_TRANSLATORS = 1; + private static final int POS_SPECIAL_THANKS = 2; + private static final int TOTAL_COUNT = 3; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + setHasOptionsMenu(true); + + View rootView = inflater.inflate(R.layout.pager_fragment, container, false); + ViewPager2 viewPager = rootView.findViewById(R.id.viewpager); + viewPager.setAdapter(new StatisticsPagerAdapter(this)); + // Give the TabLayout the ViewPager + TabLayout tabLayout = rootView.findViewById(R.id.sliding_tabs); + new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { + switch (position) { + case POS_DEVELOPERS: + tab.setText(R.string.developers); + break; + case POS_TRANSLATORS: + tab.setText(R.string.translators); + break; + case POS_SPECIAL_THANKS: + tab.setText(R.string.special_thanks); + break; + default: + break; + } + }).attach(); + + rootView.findViewById(R.id.toolbar).setVisibility(View.GONE); + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.contributors); + } + + public static class StatisticsPagerAdapter extends FragmentStateAdapter { + + StatisticsPagerAdapter(@NonNull Fragment fragment) { + super(fragment); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + switch (position) { + case POS_TRANSLATORS: + return new TranslatorsFragment(); + case POS_SPECIAL_THANKS: + return new SpecialThanksFragment(); + default: + case POS_DEVELOPERS: + return new DevelopersFragment(); + } + } + + @Override + public int getItemCount() { + return TOTAL_COUNT; + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java index 62a5eb306..60d9f95dd 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.fragment.preferences; +package de.danoeh.antennapod.fragment.preferences.about; import android.os.Bundle; import android.view.View; @@ -19,7 +19,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; -public class AboutDevelopersFragment extends ListFragment { +public class DevelopersFragment extends ListFragment { private Disposable developersLoader; @Override @@ -44,7 +44,7 @@ public class AboutDevelopersFragment extends ListFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe( developers -> setListAdapter(new SimpleIconListAdapter<>(getContext(), developers)), - error -> Toast.makeText(getContext(), "Error while loading developers", Toast.LENGTH_LONG).show() + error -> Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show() ); } @@ -56,10 +56,4 @@ public class AboutDevelopersFragment extends ListFragment { developersLoader.dispose(); } } - - @Override - public void onStart() { - super.onStart(); - ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.developers); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java index 536d11e01..97565a613 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.fragment.preferences; +package de.danoeh.antennapod.fragment.preferences.about; import android.os.Bundle; import android.view.View; @@ -28,7 +28,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; -public class AboutLicensesFragment extends ListFragment { +public class LicensesFragment extends ListFragment { private Disposable licensesLoader; private final ArrayList<LicenseItem> licenses = new ArrayList<>(); @@ -59,7 +59,7 @@ public class AboutLicensesFragment extends ListFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe( developers -> setListAdapter(new SimpleIconListAdapter<LicenseItem>(getContext(), developers)), - error -> Toast.makeText(getContext(), "Error while loading licenses", Toast.LENGTH_LONG).show() + error -> Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show() ); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java new file mode 100644 index 000000000..6db1389ea --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java @@ -0,0 +1,58 @@ +package de.danoeh.antennapod.fragment.preferences.about; + +import android.os.Bundle; +import android.view.View; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.ListFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.adapter.SimpleIconListAdapter; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; + +public class SpecialThanksFragment extends ListFragment { + private Disposable translatorsLoader; + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setDivider(null); + getListView().setSelector(android.R.color.transparent); + + translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> { + ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + getContext().getAssets().open("special_thanks.csv"))); + String line; + while ((line = reader.readLine()) != null) { + String[] info = line.split(";"); + translators.add(new SimpleIconListAdapter.ListItem(info[0], info[1], info[2])); + } + emitter.onSuccess(translators); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)), + error -> Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show() + ); + + } + + @Override + public void onStop() { + super.onStop(); + if (translatorsLoader != null) { + translatorsLoader.dispose(); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java index 914dbb9a2..e8d8e113b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.fragment.preferences; +package de.danoeh.antennapod.fragment.preferences.about; import android.os.Bundle; import android.view.View; @@ -19,7 +19,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; -public class AboutTranslatorsFragment extends ListFragment { +public class TranslatorsFragment extends ListFragment { private Disposable translatorsLoader; @Override @@ -43,7 +43,7 @@ public class AboutTranslatorsFragment extends ListFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe( translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)), - error -> Toast.makeText(getContext(), "Error while loading translators", Toast.LENGTH_LONG).show() + error -> Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show() ); } @@ -55,10 +55,4 @@ public class AboutTranslatorsFragment extends ListFragment { translatorsLoader.dispose(); } } - - @Override - public void onStart() { - super.onStart(); - ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.translators); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java index 76091327d..6b3c99975 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java @@ -8,6 +8,7 @@ import androidx.appcompat.widget.SearchView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.fragment.SearchFragment; /** @@ -30,15 +31,16 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte ta.recycle(); } - public static void setupSearchItem(Menu menu, MainActivity activity, long feedId) { + public static void setupSearchItem(Menu menu, MainActivity activity, long feedId, String feedTitle) { MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) searchItem.getActionView(); + sv.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, android.R.attr.windowBackground)); sv.setQueryHint(activity.getString(R.string.search_label)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { sv.clearFocus(); - activity.loadChildFragment(SearchFragment.newInstance(s, feedId)); + activity.loadChildFragment(SearchFragment.newInstance(s, feedId, feedTitle)); searchItem.collapseActionView(); return true; } diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java index 2a157c23b..64dd03b00 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java @@ -5,12 +5,11 @@ import android.content.SharedPreferences; import androidx.preference.PreferenceManager; import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.CrashReportWriter; +import de.danoeh.antennapod.error.CrashReportWriter; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; -import de.danoeh.antennapod.core.util.gui.NotificationUtils; public class PreferenceUpgrader { private static final String PREF_CONFIGURED_VERSION = "version_code"; diff --git a/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java b/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java index 8e8d98fc9..1b96c7c4f 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java +++ b/app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java @@ -47,11 +47,11 @@ public class LockableBottomSheetBehavior<V extends View> extends ViewPagerBottom @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, - View target, int nestedScrollAxes) { + View target, int axes, int type) { boolean handled = false; if (!isLocked) { - handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); + handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type); } return handled; @@ -59,16 +59,16 @@ public class LockableBottomSheetBehavior<V extends View> extends ViewPagerBottom @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, - int dx, int dy, int[] consumed) { + int dx, int dy, int[] consumed, int type) { if (!isLocked) { - super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); + super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type); } } @Override - public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { + public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int type) { if (!isLocked) { - super.onStopNestedScroll(coordinatorLayout, child, target); + super.onStopNestedScroll(coordinatorLayout, child, target, type); } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java index 163100fff..37d8db03e 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java +++ b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java @@ -1,7 +1,9 @@ package de.danoeh.antennapod.view; import android.content.Context; -import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.view.ContextThemeWrapper; import androidx.appcompat.widget.Toolbar; import androidx.core.view.ViewCompat; @@ -33,16 +35,20 @@ public abstract class ToolbarIconTintManager implements AppBarLayout.OnOffsetCha public void updateTint() { if (isTinted) { doTint(new ContextThemeWrapper(context, R.style.Theme_AntennaPod_Dark)); - if (toolbar.getNavigationIcon() != null) { // Tablets do not show a navigation icon - toolbar.getNavigationIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); - } - toolbar.getOverflowIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); + safeSetColorFilter(toolbar.getNavigationIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); + safeSetColorFilter(toolbar.getOverflowIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); + safeSetColorFilter(toolbar.getCollapseIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); } else { doTint(context); - if (toolbar.getNavigationIcon() != null) { // Tablets do not show a navigation icon - toolbar.getNavigationIcon().clearColorFilter(); - } - toolbar.getOverflowIcon().clearColorFilter(); + safeSetColorFilter(toolbar.getNavigationIcon(), null); + safeSetColorFilter(toolbar.getOverflowIcon(), null); + safeSetColorFilter(toolbar.getCollapseIcon(), null); + } + } + + private void safeSetColorFilter(Drawable icon, PorterDuffColorFilter filter) { + if (icon != null) { + icon.setColorFilter(filter); } } |