summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/danoeh')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java57
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FeedFilterDialog.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java82
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastIndexPodcastSearcher.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/error/CrashReportWriter.java (renamed from app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java)11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/error/RxJavaErrorHandlerSetup.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java110
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java79
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/AboutFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java)15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/DevelopersFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java)12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/LicensesFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java)6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/SpecialThanksFragment.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/TranslatorsFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java)12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/LockableBottomSheetBehavior.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java24
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);
}
}