summaryrefslogtreecommitdiff
path: root/app/src/main/java/de
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java218
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java (renamed from app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java)76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java111
-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.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java118
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java139
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java192
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java136
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java283
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java227
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java105
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PieChartView.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/ShownotesWebView.java167
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SwipeGestureDetector.java44
33 files changed, 1000 insertions, 1155 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
deleted file mode 100644
index f85a1cd77..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
-import android.view.MenuItem;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.FileChannel;
-import java.util.Arrays;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-/**
- * Displays the 'import/export' screen
- */
-public class ImportExportActivity extends AppCompatActivity {
- private static final int REQUEST_CODE_RESTORE = 43;
- private static final int REQUEST_CODE_BACKUP_DOCUMENT = 44;
- private static final String EXPORT_FILENAME = "AntennaPodBackup.db";
- private static final String TAG = ImportExportActivity.class.getSimpleName();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayShowHomeEnabled(true);
- }
- setContentView(R.layout.import_export_activity);
-
- findViewById(R.id.button_export).setOnClickListener(view -> backup());
- findViewById(R.id.button_import).setOnClickListener(view -> restore());
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void backup() {
- if (Build.VERSION.SDK_INT >= 19) {
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType("application/x-sqlite3")
- .putExtra(Intent.EXTRA_TITLE, EXPORT_FILENAME);
-
- startActivityForResult(intent, REQUEST_CODE_BACKUP_DOCUMENT);
- } else {
- try {
- File sd = Environment.getExternalStorageDirectory();
- File backupDB = new File(sd, EXPORT_FILENAME);
- writeBackupTo(new FileOutputStream(backupDB));
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
- }
- }
- }
-
- private void restore() {
- if (Build.VERSION.SDK_INT >= 19) {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.setType("*/*");
- startActivityForResult(intent, REQUEST_CODE_RESTORE);
- } else {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("*/*");
- startActivityForResult(Intent.createChooser(intent,
- getString(R.string.import_select_file)), REQUEST_CODE_RESTORE);
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
- if (resultCode != RESULT_OK || resultData == null) {
- return;
- }
- Uri uri = resultData.getData();
-
- if (requestCode == REQUEST_CODE_RESTORE) {
- restoreFrom(uri);
- } else if (requestCode == REQUEST_CODE_BACKUP_DOCUMENT) {
- backupToDocument(uri);
- }
- }
-
- private void restoreFrom(Uri inputUri) {
- InputStream inputStream = null;
- try {
- if (!validateDB(inputUri)) {
- displayBadFileDialog();
- return;
- }
-
- File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
- inputStream = getContentResolver().openInputStream(inputUri);
- FileUtils.copyInputStreamToFile(inputStream, currentDB);
- displayImportSuccessDialog();
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
- } finally {
- IOUtils.closeQuietly(inputStream);
- }
- }
-
- private static final byte[] SQLITE3_MAGIC = "SQLite format 3\0".getBytes();
- private boolean validateDB(Uri inputUri) throws IOException {
- try (InputStream inputStream = getContentResolver().openInputStream(inputUri)) {
- byte[] magicBuf = new byte[SQLITE3_MAGIC.length];
- if (inputStream.read(magicBuf) == magicBuf.length) {
- return Arrays.equals(SQLITE3_MAGIC, magicBuf);
- }
- }
-
- return false;
- }
-
- private void displayBadFileDialog() {
- AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
- d.setMessage(R.string.import_bad_file)
- .setCancelable(false)
- .setPositiveButton(android.R.string.ok, ((dialogInterface, i) -> {
- // do nothing
- }))
- .show();
- }
-
- private void displayImportSuccessDialog() {
- AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
- d.setMessage(R.string.import_ok);
- d.setCancelable(false);
- d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
- Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
- ComponentName cn = intent.getComponent();
- Intent mainIntent = Intent.makeRestartActivityTask(cn);
- startActivity(mainIntent);
- });
- d.show();
- }
-
- private void backupToDocument(Uri uri) {
- ParcelFileDescriptor pfd = null;
- FileOutputStream fileOutputStream = null;
- try {
- pfd = getContentResolver().openFileDescriptor(uri, "w");
- fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
- writeBackupTo(fileOutputStream);
-
- Snackbar.make(findViewById(R.id.import_export_layout),
- R.string.export_ok, Snackbar.LENGTH_SHORT).show();
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
- } finally {
- IOUtils.closeQuietly(fileOutputStream);
-
- if (pfd != null) {
- try {
- pfd.close();
- } catch (IOException e) {
- Log.d(TAG, "Unable to close ParcelFileDescriptor");
- }
- }
- }
- }
-
- private void writeBackupTo(FileOutputStream outFileStream) {
- FileChannel src = null;
- FileChannel dst = null;
- try {
- File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
-
- if (currentDB.exists()) {
- src = new FileInputStream(currentDB).getChannel();
- dst = outFileStream.getChannel();
- dst.transferFrom(src, 0, src.size());
-
- Snackbar.make(findViewById(R.id.import_export_layout),
- R.string.export_ok, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(findViewById(R.id.import_export_layout),
- "Can not access current database", Snackbar.LENGTH_SHORT).show();
- }
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
- } finally {
- IOUtils.closeQuietly(src);
- IOUtils.closeQuietly(dst);
- }
- }
-}
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 fab84078e..62fd4b515 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -111,20 +111,13 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private Toolbar toolbar;
private ExternalPlayerFragment externalPlayerFragment;
private DrawerLayout drawerLayout;
-
private View navDrawer;
private ListView navList;
private NavListAdapter navAdapter;
private int mPosition = -1;
-
private ActionBarDrawerToggle drawerToggle;
-
private CharSequence currentTitle;
-
- private ProgressDialog pd;
-
private Disposable disposable;
-
private long lastBackButtonPressTime = 0;
@NonNull
@@ -282,10 +275,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
- public List<Feed> getFeeds() {
- return (navDrawerData != null) ? navDrawerData.feeds : null;
- }
-
private void loadFragment(int index, Bundle args) {
Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")");
if (index < navAdapter.getSubscriptionOffset()) {
@@ -518,9 +507,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
if (disposable != null) {
disposable.dispose();
}
- if(pd != null) {
- pd.dismiss();
- }
}
@@ -849,9 +835,4 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
super.onNewIntent(intent);
setIntent(intent);
}
-
- @VisibleForTesting
- public void updateNavDrawer() {
- navAdapter.notifyDataSetChanged();
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 538ed1231..a5ae5e58c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -358,10 +358,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
}
- boolean sleepTimerSet = controller.sleepTimerActive();
- boolean sleepTimerNotSet = controller.sleepTimerNotActive();
- menu.findItem(R.id.set_sleeptimer_item).setVisible(sleepTimerNotSet);
- menu.findItem(R.id.disable_sleeptimer_item).setVisible(sleepTimerSet);
+ menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive());
+ menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
if (this instanceof AudioplayerActivity) {
int[] attrs = {R.attr.action_bar_icon_color};
@@ -422,30 +420,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
.show();
}
break;
- case R.id.disable_sleeptimer_item:
- if (controller.serviceAvailable()) {
-
- new AlertDialog.Builder(this)
- .setTitle(R.string.sleep_timer_label)
- .setMessage(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()))
- .setPositiveButton(R.string.disable_sleeptimer_label, (dialog, which)
- -> controller.disableSleepTimer())
- .setNegativeButton(R.string.cancel_label, null)
- .show();
- }
- break;
+ case R.id.disable_sleeptimer_item: // Fall-through
case R.id.set_sleeptimer_item:
- if (controller.serviceAvailable()) {
- SleepTimerDialog td = new SleepTimerDialog(this) {
- @Override
- public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
- controller.setSleepTimer(millis, shakeToReset, vibrate);
- }
- };
- td.createNewDialog().show();
- }
+ new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog");
break;
case R.id.audio_controls:
boolean isPlayingVideo = controller.getMedia().getMediaType() == MediaType.VIDEO;
@@ -793,7 +770,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private float prog;
@Override
- public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
+ public void onProgressChanged(SeekBar seekBar,int progress, boolean fromUser) {
if (controller == null || txtvLength == null) {
return;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 21f4ad5be..75819425c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -28,8 +28,6 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
-import com.viewpagerindicator.CirclePageIndicator;
-
import java.util.List;
import de.danoeh.antennapod.R;
@@ -38,6 +36,7 @@ import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -59,6 +58,7 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
+import de.danoeh.antennapod.view.PagerIndicatorView;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -102,6 +102,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private int mPosition = -1;
private ViewPager pager;
+ private PagerIndicatorView pageIndicator;
private MediaplayerInfoPagerAdapter pagerAdapter;
private Disposable disposable;
@@ -261,8 +262,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pager.setOffscreenPageLimit(3);
pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
- CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator);
+ pageIndicator = findViewById(R.id.page_indicator);
pageIndicator.setViewPager(pager);
+ pageIndicator.setOnClickListener(v
+ -> pager.setCurrentItem((pager.getCurrentItem() + 1) % pager.getChildCount()));
loadLastFragment();
pager.onSaveInstanceState();
@@ -270,6 +273,16 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
@Override
+ boolean loadMediaInfo() {
+ if (controller != null && controller.getMedia() != null) {
+ List<Chapter> chapters = controller.getMedia().getChapters();
+ boolean hasChapters = chapters != null && !chapters.isEmpty();
+ pageIndicator.setDisabledPage(hasChapters ? -1 : 2);
+ }
+ return super.loadMediaInfo();
+ }
+
+ @Override
protected void onReloadNotification(int notificationCode) {
if (notificationCode == PlaybackService.EXTRA_CODE_VIDEO) {
Log.d(TAG, "ReloadNotification received, switching to Videoplayer now");
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 50a8d0965..adb127acb 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -14,7 +14,6 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
@@ -48,6 +47,7 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -126,11 +126,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (feedUrl == null) {
Log.e(TAG, "feedUrl is null.");
- new AlertDialog.Builder(OnlineFeedViewActivity.this).
- setNeutralButton(android.R.string.ok,
- (dialog, which) -> finish()).
- setTitle(R.string.error_label).
- setMessage(R.string.null_value_podcast_error).create().show();
+ new AlertDialog.Builder(OnlineFeedViewActivity.this)
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> finish())
+ .setTitle(R.string.error_label)
+ .setMessage(R.string.null_value_podcast_error)
+ .show();
} else {
Log.d(TAG, "Activity was started with url " + feedUrl);
setLoadingLayout();
@@ -230,7 +230,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
url = URLChecker.prepareURL(url);
feed = new Feed(url, null);
if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, username, password));
+ feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password));
}
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
index d7a4b9517..03e6b89db 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
@@ -4,35 +4,53 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.Environment;
+import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
-import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
-
-import org.apache.commons.lang3.ArrayUtils;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-
+import androidx.core.app.ActivityCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.OpmlFeedQueuer;
import de.danoeh.antennapod.asynctask.OpmlImportWorker;
import de.danoeh.antennapod.core.export.opml.OpmlElement;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.LangUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
/**
- * Base activity for Opml Import - e.g. with code what to do afterwards
+ * Activity for Opml Import.
* */
-public class OpmlImportBaseActivity extends AppCompatActivity {
-
+public class OpmlImportActivity extends AppCompatActivity {
private static final String TAG = "OpmlImportBaseActivity";
private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
- private OpmlImportWorker importWorker;
@Nullable private Uri uri;
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+
+ Uri uri = getIntent().getData();
+ if (uri != null && uri.toString().startsWith("/")) {
+ uri = Uri.parse("file://" + uri.toString());
+ } else {
+ String extraText = getIntent().getStringExtra(Intent.EXTRA_TEXT);
+ if (extraText != null) {
+ uri = Uri.parse(extraText);
+ }
+ }
+ importUri(uri);
+ }
+
/**
* Handles the choices made by the user in the OpmlFeedChooserActivity and
* starts the OpmlFeedQueuer if necessary.
@@ -42,9 +60,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
Log.d(TAG, "Received result");
if (resultCode == RESULT_CANCELED) {
Log.d(TAG, "Activity was cancelled");
- if (finishWhenCanceled()) {
- finish();
- }
+ finish();
} else {
int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
if (selected != null && selected.length > 0) {
@@ -53,7 +69,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
- Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
+ Intent intent = new Intent(OpmlImportActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
@@ -68,7 +84,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
}
void importUri(@Nullable Uri uri) {
- if(uri == null) {
+ if (uri == null) {
new AlertDialog.Builder(this)
.setMessage(R.string.opml_import_error_no_file)
.setPositiveButton(android.R.string.ok, null)
@@ -76,9 +92,10 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
return;
}
this.uri = uri;
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
- int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
+ int permission = ActivityCompat.checkSelfPermission(this,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
requestPermission();
return;
@@ -93,9 +110,8 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
}
@Override
- public void onRequestPermissionsResult(int requestCode,
- String[] permissions,
- int[] grantResults) {
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
return;
}
@@ -113,8 +129,8 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
/** Starts the import process. */
private void startImport() {
try {
- Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
- importWorker = new OpmlImportWorker(this, mReader) {
+ Reader reader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
+ OpmlImportWorker importWorker = new OpmlImportWorker(this, reader) {
@Override
protected void onPostExecute(ArrayList<OpmlElement> result) {
@@ -123,7 +139,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
Log.d(TAG, "Parsing was successful");
OpmlImportHolder.setReadElements(result);
startActivityForResult(new Intent(
- OpmlImportBaseActivity.this,
+ OpmlImportActivity.this,
OpmlFeedChooserActivity.class), 0);
} else {
Log.d(TAG, "Parser error occurred");
@@ -140,10 +156,4 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
.show();
}
}
-
- boolean finishWhenCanceled() {
- return false;
- }
-
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
deleted file mode 100644
index 557510808..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-
-/**
- * Lets the user start the OPML-import process.
- */
-public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
-
- private static final String TAG = "OpmlImportFromIntentAct";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- Uri uri = getIntent().getData();
- if (uri != null && uri.toString().startsWith("/")) {
- uri = Uri.parse("file://" + uri.toString());
- } else {
- String extraText = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- if(extraText != null) {
- uri = Uri.parse(extraText);
- }
- }
- importUri(uri);
- }
-
- @Override
- protected boolean finishWhenCanceled() {
- return true;
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
deleted file mode 100644
index 158e3b7a4..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.StorageUtils;
-
-/**
- * Lets the user start the OPML-import process from a path
- */
-public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
-
- private static final String TAG = "OpmlImportFromPathAct";
-
- private static final int CHOOSE_OPML_FILE = 1;
-
- private Intent intentGetContentAction;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- setContentView(R.layout.opml_import);
-
- final TextView txtvHeaderExplanation = findViewById(R.id.txtvHeadingExplanation);
- final TextView txtvExplanation = findViewById(R.id.txtvExplanation);
- final TextView txtvHeaderExplanationOpenWith = findViewById(R.id.txtvHeadingExplanationOpenWith);
-
- Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem);
- butChooseFilesystem.setOnClickListener(v -> chooseFileFromExternal());
-
- int nextOption = 1;
- String optionLabel = getString(R.string.opml_import_option);
- intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
- intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
- intentGetContentAction.setType("*/*");
-
- if (IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) {
- txtvHeaderExplanation.setText(String.format(optionLabel, nextOption));
- nextOption++;
- } else {
- txtvHeaderExplanation.setVisibility(View.GONE);
- txtvExplanation.setVisibility(View.GONE);
- findViewById(R.id.divider).setVisibility(View.GONE);
- butChooseFilesystem.setVisibility(View.GONE);
- }
-
- txtvHeaderExplanationOpenWith.setText(String.format(optionLabel, nextOption));
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- StorageUtils.checkStorageAvailability(this);
- }
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return false;
- }
- }
-
- private void chooseFileFromExternal() {
- try {
- startActivityForResult(intentGetContentAction, CHOOSE_OPML_FILE);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "No activity found. Should never happen...");
- }
- }
-
- /**
- * Gets the path of the file chosen with chooseFileToImport()
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK && requestCode == CHOOSE_OPML_FILE) {
- Uri uri = data.getData();
- if(uri != null && uri.toString().startsWith("/")) {
- uri = Uri.parse("file://" + uri.toString());
- }
- importUri(uri);
- }
- }
-
-}
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 a0f9bf6d8..dd91b7e2a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -16,6 +16,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.preferences.AutoDownloadPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.IntegrationsPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.NetworkPreferencesFragment;
@@ -59,6 +60,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
prefFragment = new NetworkPreferencesFragment();
} else if (screen == R.xml.preferences_storage) {
prefFragment = new StoragePreferencesFragment();
+ } else if (screen == R.xml.preferences_import_export) {
+ prefFragment = new ImportExportPreferencesFragment();
} else if (screen == R.xml.preferences_autodownload) {
prefFragment = new AutoDownloadPreferencesFragment();
} else if (screen == R.xml.preferences_gpodder) {
@@ -79,6 +82,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
return R.string.playback_pref;
case R.xml.preferences_storage:
return R.string.storage_pref;
+ case R.xml.preferences_import_export:
+ return R.string.import_export_pref;
case R.xml.preferences_user_interface:
return R.string.user_interface_label;
case R.xml.preferences_integrations:
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 0c6ae2645..8fb7aa73f 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -98,7 +98,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
- public void onUserLeaveHint () {
+ public void onUserLeaveHint() {
if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior()
== UserPreferences.VideoBackgroundBehavior.PICTURE_IN_PICTURE) {
compatEnterPictureInPicture();
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 227eea6e0..c49d2f39d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadStatisticsListAdapter.java
@@ -3,10 +3,12 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.view.PieChartView;
+import java.util.List;
+
/**
* Adapter for the download statistics list.
*/
@@ -27,17 +29,17 @@ public class DownloadStatisticsListAdapter extends StatisticsListAdapter {
}
@Override
- PieChartView.PieChartData generateChartData(DBReader.StatisticsData statisticsData) {
- float[] dataValues = new float[statisticsData.feeds.size()];
- for (int i = 0; i < statisticsData.feeds.size(); i++) {
- DBReader.StatisticsItem item = statisticsData.feeds.get(i);
+ PieChartView.PieChartData generateChartData(List<StatisticsItem> statisticsData) {
+ float[] dataValues = new float[statisticsData.size()];
+ for (int i = 0; i < statisticsData.size(); i++) {
+ StatisticsItem item = statisticsData.get(i);
dataValues[i] = item.totalDownloadSize;
}
return new PieChartView.PieChartData(dataValues);
}
@Override
- void onBindFeedViewHolder(StatisticsHolder holder, DBReader.StatisticsItem item) {
+ void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem item) {
holder.value.setText(Converter.byteToString(item.totalDownloadSize));
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
index 8471569d3..c5a73c53e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
@@ -4,10 +4,12 @@ import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.view.PieChartView;
+import java.util.List;
+
/**
* Adapter for the playback statistics list.
*/
@@ -34,17 +36,17 @@ public class PlaybackStatisticsListAdapter extends StatisticsListAdapter {
}
@Override
- PieChartView.PieChartData generateChartData(DBReader.StatisticsData statisticsData) {
- float[] dataValues = new float[statisticsData.feeds.size()];
- for (int i = 0; i < statisticsData.feeds.size(); i++) {
- DBReader.StatisticsItem item = statisticsData.feeds.get(i);
+ PieChartView.PieChartData generateChartData(List<StatisticsItem> statisticsData) {
+ float[] dataValues = new float[statisticsData.size()];
+ for (int i = 0; i < statisticsData.size(); i++) {
+ StatisticsItem item = statisticsData.get(i);
dataValues[i] = countAll ? item.timePlayedCountAll : item.timePlayed;
}
return new PieChartView.PieChartData(dataValues);
}
@Override
- void onBindFeedViewHolder(StatisticsHolder holder, DBReader.StatisticsItem statsItem) {
+ void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem statsItem) {
long time = countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed;
holder.value.setText(Converter.shortLocalizedDuration(context, time));
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
index db65190f2..5f019d1db 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
@@ -14,9 +14,11 @@ import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.view.PieChartView;
+import java.util.List;
+
/**
* Parent Adapter for the playback and download statistics list.
*/
@@ -24,7 +26,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
private static final int TYPE_HEADER = 0;
private static final int TYPE_FEED = 1;
final Context context;
- private DBReader.StatisticsData statisticsData;
+ private List<StatisticsItem> statisticsData;
PieChartView.PieChartData pieChartData;
StatisticsListAdapter(Context context) {
@@ -33,14 +35,14 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
@Override
public int getItemCount() {
- return statisticsData.feeds.size() + 1;
+ return statisticsData.size() + 1;
}
- public DBReader.StatisticsItem getItem(int position) {
+ public StatisticsItem getItem(int position) {
if (position == 0) {
return null;
}
- return statisticsData.feeds.get(position - 1);
+ return statisticsData.get(position - 1);
}
@Override
@@ -69,7 +71,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
holder.totalTime.setText(getHeaderValue());
} else {
StatisticsHolder holder = (StatisticsHolder) h;
- DBReader.StatisticsItem statsItem = statisticsData.feeds.get(position - 1);
+ StatisticsItem statsItem = statisticsData.get(position - 1);
Glide.with(context)
.load(statsItem.feed.getImageLocation())
.apply(new RequestOptions()
@@ -86,8 +88,8 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
}
}
- public void update(DBReader.StatisticsData statistics) {
- this.statisticsData = statistics;
+ public void update(List<StatisticsItem> statistics) {
+ statisticsData = statistics;
pieChartData = generateChartData(statistics);
notifyDataSetChanged();
}
@@ -122,7 +124,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle
abstract String getHeaderValue();
- abstract PieChartView.PieChartData generateChartData(DBReader.StatisticsData statisticsData);
+ abstract PieChartView.PieChartData generateChartData(List<StatisticsItem> statisticsData);
- abstract void onBindFeedViewHolder(StatisticsHolder holder, DBReader.StatisticsItem item);
+ abstract void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem item);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
index 339a98dfa..3ac05e842 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
@@ -45,7 +45,7 @@ public class DocumentFileExportWorker {
throw new IOException();
}
writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8);
- exportWriter.writeDocument(DBReader.getFeedList(), writer);
+ exportWriter.writeDocument(DBReader.getFeedList(), writer, context);
subscriber.onNext(output);
} catch (IOException e) {
subscriber.onError(e);
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
index 40b101ddf..f81a52402 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.asynctask;
+import android.content.Context;
import androidx.annotation.NonNull;
import android.util.Log;
@@ -25,15 +26,17 @@ public class ExportWorker {
private final @NonNull ExportWriter exportWriter;
private final @NonNull File output;
+ private final Context context;
- public ExportWorker(@NonNull ExportWriter exportWriter) {
+ public ExportWorker(@NonNull ExportWriter exportWriter, Context context) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
- DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
+ DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()), context);
}
- private ExportWorker(@NonNull ExportWriter exportWriter, @NonNull File output) {
+ private ExportWorker(@NonNull ExportWriter exportWriter, @NonNull File output, Context context) {
this.exportWriter = exportWriter;
this.output = output;
+ this.context = context;
}
public Observable<File> exportObservable() {
@@ -45,7 +48,7 @@ public class ExportWorker {
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8);
- exportWriter.writeDocument(DBReader.getFeedList(), writer);
+ exportWriter.writeDocument(DBReader.getFeedList(), writer, context);
subscriber.onNext(output);
} catch (IOException e) {
subscriber.onError(e);
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
index b88b58537..e037eb392 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
@@ -16,84 +16,78 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.export.opml.OpmlElement;
import de.danoeh.antennapod.core.export.opml.OpmlReader;
-public class OpmlImportWorker extends
- AsyncTask<Void, Void, ArrayList<OpmlElement>> {
- private static final String TAG = "OpmlImportWorker";
+public class OpmlImportWorker extends AsyncTask<Void, Void, ArrayList<OpmlElement>> {
+ private static final String TAG = "OpmlImportWorker";
- private final Context context;
- private Exception exception;
+ private final Context context;
+ private Exception exception;
+ private ProgressDialog progDialog;
- private ProgressDialog progDialog;
-
- private final Reader mReader;
+ private final Reader reader;
public OpmlImportWorker(Context context, Reader reader) {
super();
this.context = context;
- this.mReader=reader;
+ this.reader = reader;
}
- @Override
- protected ArrayList<OpmlElement> doInBackground(Void... params) {
- Log.d(TAG, "Starting background work");
+ @Override
+ protected ArrayList<OpmlElement> doInBackground(Void... params) {
+ Log.d(TAG, "Starting background work");
- if (mReader==null) {
+ if (reader == null) {
return null;
}
- OpmlReader opmlReader = new OpmlReader();
- try {
- ArrayList<OpmlElement> result = opmlReader.readDocument(mReader);
- mReader.close();
- return result;
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- exception = e;
- return null;
- } catch (IOException e) {
- e.printStackTrace();
- exception = e;
- return null;
- }
-
- }
-
- @Override
- protected void onPostExecute(ArrayList<OpmlElement> result) {
- if (mReader != null) {
+ OpmlReader opmlReader = new OpmlReader();
+ try {
+ ArrayList<OpmlElement> result = opmlReader.readDocument(reader);
+ reader.close();
+ return result;
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ exception = e;
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ exception = e;
+ return null;
+ }
+
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<OpmlElement> result) {
+ if (reader != null) {
try {
- mReader.close();
+ reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- progDialog.dismiss();
- if (exception != null) {
- Log.d(TAG, "An error occurred while trying to parse the opml document");
- AlertDialog.Builder alert = new AlertDialog.Builder(context);
- alert.setTitle(R.string.error_label);
- alert.setMessage(context.getString(R.string.opml_reader_error)
- + exception.getMessage());
- alert.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- alert.create().show();
- }
- }
-
- @Override
- protected void onPreExecute() {
- progDialog = new ProgressDialog(context);
- progDialog.setMessage(context.getString(R.string.reading_opml_label));
- progDialog.setIndeterminate(true);
- progDialog.setCancelable(false);
- progDialog.show();
- }
-
- public boolean wasSuccessful() {
- return exception != null;
- }
-
- public void executeAsync() {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
- }
+ progDialog.dismiss();
+ if (exception != null) {
+ Log.d(TAG, "An error occurred while trying to parse the opml document");
+ AlertDialog.Builder alert = new AlertDialog.Builder(context);
+ alert.setTitle(R.string.error_label);
+ alert.setMessage(context.getString(R.string.opml_reader_error)
+ + exception.getMessage());
+ alert.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.create().show();
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ progDialog = new ProgressDialog(context);
+ progDialog.setMessage(context.getString(R.string.please_wait));
+ progDialog.setIndeterminate(true);
+ progDialog.setCancelable(false);
+ progDialog.show();
+ }
+
+ public void executeAsync() {
+ executeOnExecutor(THREAD_POOL_EXECUTOR);
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index 34b102ca8..cbba1637f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -245,7 +245,7 @@ public class EpisodesApplyActionFragment extends Fragment {
}
@Override
- public void onPrepareOptionsMenu (Menu menu) {
+ public void onPrepareOptionsMenu(Menu menu) {
// Prepare icon for select toggle button
int[] icon = new int[1];
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 8d176c708..d36f97c7a 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -1,67 +1,101 @@
package de.danoeh.antennapod.dialog;
+import android.app.Dialog;
import android.content.Context;
+import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.Spinner;
+import android.widget.TextView;
import android.widget.Toast;
-
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
-import org.greenrobot.eventbus.EventBus;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
-public abstract class SleepTimerDialog {
-
- private static final String TAG = SleepTimerDialog.class.getSimpleName();
+import java.util.concurrent.TimeUnit;
- private final Context context;
+public class SleepTimerDialog extends DialogFragment {
+ private PlaybackController controller;
+ private Disposable timeUpdater;
- private AlertDialog dialog;
private EditText etxtTime;
private Spinner spTimeUnit;
private CheckBox cbShakeToReset;
private CheckBox cbVibrate;
private CheckBox chAutoEnable;
+ private LinearLayout timeSetup;
+ private LinearLayout timeDisplay;
+ private TextView time;
+ public SleepTimerDialog() {
- protected SleepTimerDialog(Context context) {
- this.context = context;
}
- public AlertDialog createNewDialog() {
- View content = View.inflate(context, R.layout.time_dialog, null);
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.set_sleeptimer_label);
- builder.setView(content);
- builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> dialog.dismiss());
- builder.setPositiveButton(R.string.set_sleeptimer_label, (dialog, which) -> {
- try {
- savePreferences();
- long input = SleepTimerPreferences.timerMillis();
- onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
- dialog.dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public void setupGUI() {
+ updateTime();
}
- });
- dialog = builder.create();
+
+ @Override
+ public void onSleepTimerUpdate() {
+ updateTime();
+ }
+ };
+ controller.init();
+ timeUpdater = Observable.interval(1, TimeUnit.SECONDS)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(tick -> updateTime());
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (controller != null) {
+ controller.release();
+ }
+ if (timeUpdater != null) {
+ timeUpdater.dispose();
+ }
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ View content = View.inflate(getContext(), R.layout.time_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.sleep_timer_label);
+ builder.setView(content);
+ builder.setPositiveButton(android.R.string.ok, null);
etxtTime = content.findViewById(R.id.etxtTime);
spTimeUnit = content.findViewById(R.id.spTimeUnit);
cbShakeToReset = content.findViewById(R.id.cbShakeToReset);
cbVibrate = content.findViewById(R.id.cbVibrate);
chAutoEnable = content.findViewById(R.id.chAutoEnable);
+ timeSetup = content.findViewById(R.id.timeSetup);
+ timeDisplay = content.findViewById(R.id.timeDisplay);
+ time = content.findViewById(R.id.time);
+ AlertDialog dialog = builder.create();
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {
@Override
@@ -78,15 +112,15 @@ public abstract class SleepTimerDialog {
}
});
etxtTime.postDelayed(() -> {
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
}, 100);
String[] spinnerContent = new String[] {
- context.getString(R.string.time_seconds),
- context.getString(R.string.time_minutes),
- context.getString(R.string.time_hours) };
- ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(context,
+ getString(R.string.time_seconds),
+ getString(R.string.time_minutes),
+ getString(R.string.time_hours) };
+ ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(),
android.R.layout.simple_spinner_item, spinnerContent);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spTimeUnit.setAdapter(spinnerAdapter);
@@ -96,16 +130,33 @@ public abstract class SleepTimerDialog {
cbVibrate.setChecked(SleepTimerPreferences.vibrate());
chAutoEnable.setChecked(SleepTimerPreferences.autoEnable());
- chAutoEnable.setOnCheckedChangeListener((compoundButton, isChecked) -> {
- SleepTimerPreferences.setAutoEnable(isChecked);
- int messageString = isChecked ? R.string.sleep_timer_enabled_label : R.string.sleep_timer_disabled_label;
- EventBus.getDefault().post(new MessageEvent(context.getString(messageString)));
+ chAutoEnable.setOnCheckedChangeListener((compoundButton, isChecked)
+ -> SleepTimerPreferences.setAutoEnable(isChecked));
+ Button disableButton = content.findViewById(R.id.disableSleeptimerButton);
+ disableButton.setOnClickListener(v -> {
+ if (controller != null) {
+ controller.disableSleepTimer();
+ }
+ });
+ Button setButton = content.findViewById(R.id.setSleeptimerButton);
+ setButton.setOnClickListener(v -> {
+ if (!PlaybackService.isRunning) {
+ Toast.makeText(getContext(), R.string.no_media_playing_label, Toast.LENGTH_LONG).show();
+ }
+ try {
+ savePreferences();
+ long time = SleepTimerPreferences.timerMillis();
+ if (controller != null) {
+ controller.setSleepTimer(time, cbShakeToReset.isChecked(), cbVibrate.isChecked());
+ }
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast.makeText(getContext(), R.string.time_dialog_invalid_input, Toast.LENGTH_LONG).show();
+ }
});
return dialog;
}
- public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate);
-
private void savePreferences() {
SleepTimerPreferences.setLastTimer(etxtTime.getText().toString(),
spTimeUnit.getSelectedItemPosition());
@@ -114,4 +165,12 @@ public abstract class SleepTimerDialog {
SleepTimerPreferences.setAutoEnable(chAutoEnable.isChecked());
}
+ private void updateTime() {
+ if (controller == null) {
+ return;
+ }
+ timeSetup.setVisibility(controller.sleepTimerActive() ? View.GONE : View.VISIBLE);
+ timeDisplay.setVisibility(controller.sleepTimerActive() ? View.VISIBLE : View.GONE);
+ time.setText(Converter.getDurationStringLong((int) controller.getSleepTimerTimeLeft()));
+ }
}
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 2cfe7c1e8..343cf76ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -1,7 +1,11 @@
package de.danoeh.antennapod.fragment;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import androidx.fragment.app.Fragment;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -14,7 +18,7 @@ import android.widget.EditText;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
-import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.activity.OpmlImportActivity;
import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment;
/**
@@ -28,6 +32,7 @@ public class AddFeedFragment extends Fragment {
* Preset value for url text field.
*/
private static final String ARG_FEED_URL = "feedurl";
+ private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
private EditText combinedFeedSearchBox;
private MainActivity activity;
@@ -44,8 +49,16 @@ public class AddFeedFragment extends Fragment {
setupSeachBox(root);
View butOpmlImport = root.findViewById(R.id.btn_opml_import);
- butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
- OpmlImportFromPathActivity.class)));
+ butOpmlImport.setOnClickListener(v -> {
+ try {
+ Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
+ intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
+ intentGetContentAction.setType("*/*");
+ startActivityForResult(intentGetContentAction, REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ });
root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch());
return root;
}
@@ -130,4 +143,18 @@ public class AddFeedFragment extends Fragment {
// persist. mfietz thinks this causes the ActionBar to be invalidated
setHasOptionsMenu(true);
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK || data == null) {
+ return;
+ }
+ Uri uri = data.getData();
+
+ if (requestCode == REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH) {
+ Intent intent = new Intent(getContext(), OpmlImportActivity.class);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index bb1f8f8e9..63160bb2c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -3,16 +3,19 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import androidx.preference.SwitchPreference;
+import android.util.Log;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceFragmentCompat;
-import android.util.Log;
+import androidx.preference.SwitchPreference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent;
+import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -23,6 +26,8 @@ import io.reactivex.MaybeOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
@@ -32,7 +37,8 @@ import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
public class FeedSettingsFragment extends PreferenceFragmentCompat {
private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter";
private static final String PREF_FEED_PLAYBACK_SPEED = "feedPlaybackSpeed";
- private static final DecimalFormat decimalFormat = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
+ private static final DecimalFormat SPEED_FORMAT =
+ new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedSettingsFragment";
@@ -73,11 +79,13 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
setupAutoDownloadPreference();
setupKeepUpdatedPreference();
setupAutoDeletePreference();
+ setupVolumeReductionPreferences();
setupAuthentificationPreference();
setupEpisodeFilterPreference();
setupPlaybackSpeedPreference();
updateAutoDeleteSummary();
+ updateVolumeReductionValue();
updateAutoDownloadEnabled();
updatePlaybackSpeedPreference();
}, error -> Log.d(TAG, Log.getStackTraceString(error)), () -> { });
@@ -112,7 +120,7 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
String[] speeds = UserPreferences.getPlaybackSpeedArray();
String[] values = new String[speeds.length + 1];
- values[0] = decimalFormat.format(SPEED_USE_GLOBAL);
+ values[0] = SPEED_FORMAT.format(SPEED_USE_GLOBAL);
String[] entries = new String[speeds.length + 1];
entries[0] = getString(R.string.feed_auto_download_global);
@@ -122,11 +130,12 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
feedPlaybackSpeedPreference.setEntryValues(values);
feedPlaybackSpeedPreference.setEntries(entries);
-
feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
feed.savePreferences();
updatePlaybackSpeedPreference();
+ EventBus.getDefault().post(
+ new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
return false;
});
}
@@ -184,7 +193,7 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
float speedValue = feedPreferences.getFeedPlaybackSpeed();
- feedPlaybackSpeedPreference.setValue(decimalFormat.format(speedValue));
+ feedPlaybackSpeedPreference.setValue(SPEED_FORMAT.format(speedValue));
}
private void updateAutoDeleteSummary() {
@@ -206,6 +215,44 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
}
}
+ private void setupVolumeReductionPreferences() {
+ ListPreference volumeReductionPreference = (ListPreference) findPreference("volumeReduction");
+ volumeReductionPreference.setOnPreferenceChangeListener((preference, newValue) -> {
+ switch ((String) newValue) {
+ case "off":
+ feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.OFF);
+ break;
+ case "light":
+ feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+ break;
+ case "heavy":
+ feedPreferences.setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
+ break;
+ }
+ feed.savePreferences();
+ updateVolumeReductionValue();
+ EventBus.getDefault().post(
+ new VolumeAdaptionChangedEvent(feedPreferences.getVolumeAdaptionSetting(), feed.getId()));
+ return false;
+ });
+ }
+
+ private void updateVolumeReductionValue() {
+ ListPreference volumeReductionPreference = (ListPreference) findPreference("volumeReduction");
+
+ switch (feedPreferences.getVolumeAdaptionSetting()) {
+ case OFF:
+ volumeReductionPreference.setValue("off");
+ break;
+ case LIGHT_REDUCTION:
+ volumeReductionPreference.setValue("light");
+ break;
+ case HEAVY_REDUCTION:
+ volumeReductionPreference.setValue("heavy");
+ break;
+ }
+ }
+
private void setupKeepUpdatedPreference() {
SwitchPreference pref = (SwitchPreference) findPreference("keepUpdated");
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 85978b761..256615199 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -1,40 +1,18 @@
package de.danoeh.antennapod.fragment;
-import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.Toast;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MediaplayerInfoActivity;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.ShareUtils;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import de.danoeh.antennapod.view.ShownotesWebView;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -44,72 +22,29 @@ import io.reactivex.schedulers.Schedulers;
* Displays the description of a Playable object in a Webview.
*/
public class ItemDescriptionFragment extends Fragment {
-
private static final String TAG = "ItemDescriptionFragment";
private static final String PREF = "ItemDescriptionFragmentPrefs";
private static final String PREF_SCROLL_Y = "prefScrollY";
private static final String PREF_PLAYABLE_ID = "prefPlayableId";
- private WebView webvDescription;
+ private ShownotesWebView webvDescription;
private Disposable webViewLoader;
private PlaybackController controller;
- /**
- * URL that was selected via long-press.
- */
- private String selectedURL;
-
-
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "Creating view");
- webvDescription = new WebView(getActivity().getApplicationContext());
- webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-
- TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[]
- {android.R.attr.colorBackground});
- boolean black = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark
- || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack;
- int backgroundColor = ta.getColor(0, black ? Color.BLACK : Color.WHITE);
-
- ta.recycle();
- webvDescription.setBackgroundColor(backgroundColor);
- if (!NetworkUtils.networkAvailable()) {
- webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
- // Use cached resources, even if they have expired
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
- }
-
- webvDescription.getSettings().setUseWideViewPort(false);
- webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
- webvDescription.getSettings().setLoadWithOverviewMode(true);
- webvDescription.setOnLongClickListener(webViewLongClickListener);
- webvDescription.setWebViewClient(new WebViewClient() {
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (Timeline.isTimecodeLink(url)) {
- onTimecodeLinkSelected(url);
- } else {
- IntentUtils.openInBrowser(getContext(), url);
- }
- return true;
+ webvDescription = new ShownotesWebView(getActivity().getApplicationContext());
+ webvDescription.setTimecodeSelectedListener(time -> {
+ if (controller != null) {
+ controller.seekTo(time);
}
-
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- Log.d(TAG, "Page finished");
- // Restoring the scroll position might not always work
- view.postDelayed(ItemDescriptionFragment.this::restoreFromPreference, 50);
- }
-
});
-
+ webvDescription.setPageFinishedListener(() -> {
+ // Restoring the scroll position might not always work
+ webvDescription.postDelayed(ItemDescriptionFragment.this::restoreFromPreference, 50);
+ });
registerForContextMenu(webvDescription);
return webvDescription;
}
@@ -127,91 +62,14 @@ public class ItemDescriptionFragment extends Fragment {
}
}
- private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- WebView.HitTestResult r = webvDescription.getHitTestResult();
- if (r != null
- && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
- Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
- selectedURL = r.getExtra();
- webvDescription.showContextMenu();
- return true;
- }
- selectedURL = null;
- return false;
- }
- };
-
- @SuppressLint("NewApi")
@Override
public boolean onContextItemSelected(MenuItem item) {
- boolean handled = selectedURL != null;
- if (selectedURL != null) {
- switch (item.getItemId()) {
- case R.id.open_in_browser_item:
- IntentUtils.openInBrowser(getContext(), selectedURL);
- break;
- case R.id.share_url_item:
- ShareUtils.shareLink(getActivity(), selectedURL);
- break;
- case R.id.copy_url_item:
- ClipData clipData = ClipData.newPlainText(selectedURL,
- selectedURL);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- Toast t = Toast.makeText(getActivity(),
- R.string.copied_url_msg, Toast.LENGTH_SHORT);
- t.show();
- break;
- case R.id.go_to_position_item:
- if (Timeline.isTimecodeLink(selectedURL)) {
- onTimecodeLinkSelected(selectedURL);
- } else {
- Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: " + selectedURL);
- }
- break;
- default:
- handled = false;
- break;
-
- }
- selectedURL = null;
- }
- return handled;
-
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- if (selectedURL != null) {
- super.onCreateContextMenu(menu, v, menuInfo);
- if (Timeline.isTimecodeLink(selectedURL)) {
- menu.add(Menu.NONE, R.id.go_to_position_item, Menu.NONE,
- R.string.go_to_position_label);
- menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedURL)));
- } else {
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
- }
- menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
- R.string.copy_url_label);
- menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
- R.string.share_url_label);
- menu.setHeaderTitle(selectedURL);
- }
- }
+ return webvDescription.onContextItemSelected(item);
}
private void load() {
Log.d(TAG, "load()");
- if(webViewLoader != null) {
+ if (webViewLoader != null) {
webViewLoader.dispose();
}
webViewLoader = Observable.fromCallable(this::loadData)
@@ -227,7 +85,7 @@ public class ItemDescriptionFragment extends Fragment {
@NonNull
private String loadData() {
Timeline timeline = new Timeline(getActivity(), controller.getMedia());
- return timeline.processShownotes(true);
+ return timeline.processShownotes();
}
@Override
@@ -238,8 +96,7 @@ public class ItemDescriptionFragment extends Fragment {
private void savePreference() {
Log.d(TAG, "Saving preferences");
- SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
- Activity.MODE_PRIVATE);
+ SharedPreferences prefs = getActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (controller != null && controller.getMedia() != null && webvDescription != null) {
Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
@@ -251,15 +108,14 @@ public class ItemDescriptionFragment extends Fragment {
editor.putInt(PREF_SCROLL_Y, -1);
editor.putString(PREF_PLAYABLE_ID, "");
}
- editor.commit();
+ editor.apply();
}
private boolean restoreFromPreference() {
Log.d(TAG, "Restoring from preferences");
Activity activity = getActivity();
if (activity != null) {
- SharedPreferences prefs = activity.getSharedPreferences(
- PREF, Activity.MODE_PRIVATE);
+ SharedPreferences prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE);
String id = prefs.getString(PREF_PLAYABLE_ID, "");
int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
if (controller != null && scrollY != -1 && controller.getMedia() != null
@@ -274,16 +130,6 @@ public class ItemDescriptionFragment extends Fragment {
return false;
}
- private void onTimecodeLinkSelected(String link) {
- int time = Timeline.getTimecodeLinkTime(link);
- if (getActivity() != null && getActivity() instanceof MediaplayerInfoActivity) {
- PlaybackController pc = ((MediaplayerInfoActivity) getActivity()).getPlaybackController();
- if (pc != null) {
- pc.seekTo(time);
- }
- }
- }
-
@Override
public void onStart() {
super.onStart();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 7a3d034f1..e1202704a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -1,9 +1,7 @@
package de.danoeh.antennapod.fragment;
-import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
-import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -11,31 +9,22 @@ import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
-import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import android.widget.Toast;
import androidx.annotation.AttrRes;
-import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
-import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import com.joanzapata.iconify.Iconify;
-import com.joanzapata.iconify.widget.IconButton;
+import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
@@ -47,7 +36,6 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
@@ -55,10 +43,9 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import de.danoeh.antennapod.view.ShownotesWebView;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -99,7 +86,7 @@ public class ItemFragment extends Fragment {
private List<Downloader> downloaderList;
private ViewGroup root;
- private WebView webvDescription;
+ private ShownotesWebView webvDescription;
private TextView txtvPodcast;
private TextView txtvTitle;
private TextView txtvDuration;
@@ -111,11 +98,7 @@ public class ItemFragment extends Fragment {
private Button butAction2;
private Disposable disposable;
-
- /**
- * URL that was selected via long-press.
- */
- private String selectedURL;
+ private PlaybackController controller;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -134,7 +117,7 @@ public class ItemFragment extends Fragment {
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = layout.findViewById(R.id.txtvTitle);
- if(Build.VERSION.SDK_INT >= 23) {
+ if (Build.VERSION.SDK_INT >= 23) {
txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
txtvDuration = layout.findViewById(R.id.txtvDuration);
@@ -143,31 +126,11 @@ public class ItemFragment extends Fragment {
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
webvDescription = layout.findViewById(R.id.webvDescription);
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark ||
- UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webvDescription.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.black));
- }
- if (!NetworkUtils.networkAvailable()) {
- webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
- // Use cached resources, even if they have expired
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- webvDescription.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
- }
- webvDescription.getSettings().setUseWideViewPort(false);
- webvDescription.getSettings().setLayoutAlgorithm(
- WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
- webvDescription.getSettings().setLoadWithOverviewMode(true);
- webvDescription.setOnLongClickListener(webViewLongClickListener);
-
- webvDescription.setWebViewClient(new WebViewClient() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- IntentUtils.openInBrowser(getContext(), url);
- return true;
+ webvDescription.setTimecodeSelectedListener(time -> {
+ if (controller != null && item.getMedia().getIdentifier().equals(controller.getMedia().getIdentifier())) {
+ controller.seekTo(time);
+ } else {
+ Snackbar.make(getView(), R.string.play_this_to_seek_position, Snackbar.LENGTH_LONG).show();
}
});
registerForContextMenu(webvDescription);
@@ -230,6 +193,8 @@ public class ItemFragment extends Fragment {
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
+ controller = new PlaybackController(getActivity(), false);
+ controller.init();
}
@Override
@@ -245,12 +210,13 @@ public class ItemFragment extends Fragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
+ controller.release();
}
@Override
public void onDestroyView() {
super.onDestroyView();
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
if (webvDescription != null && root != null) {
@@ -364,76 +330,14 @@ public class ItemFragment extends Fragment {
}
}
- private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- WebView.HitTestResult r = webvDescription.getHitTestResult();
- if (r != null
- && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
- Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
- selectedURL = r.getExtra();
- webvDescription.showContextMenu();
- return true;
- }
- selectedURL = null;
- return false;
- }
- };
-
@Override
public boolean onContextItemSelected(MenuItem item) {
- boolean handled = selectedURL != null;
- if (selectedURL != null) {
- switch (item.getItemId()) {
- case R.id.open_in_browser_item:
- IntentUtils.openInBrowser(getContext(), selectedURL);
- break;
- case R.id.share_url_item:
- ShareUtils.shareLink(getActivity(), selectedURL);
- break;
- case R.id.copy_url_item:
- ClipData clipData = ClipData.newPlainText(selectedURL,
- selectedURL);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- Toast t = Toast.makeText(getActivity(),
- R.string.copied_url_msg, Toast.LENGTH_SHORT);
- t.show();
- break;
- default:
- handled = false;
- break;
-
- }
- selectedURL = null;
- }
- return handled;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenu.ContextMenuInfo menuInfo) {
- if (selectedURL != null) {
- super.onCreateContextMenu(menu, v, menuInfo);
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
- }
- menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
- R.string.copy_url_label);
- menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
- R.string.share_url_label);
- menu.setHeaderTitle(selectedURL);
- }
+ return webvDescription.onContextItemSelected(item);
}
private void openPodcast() {
Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
- ((MainActivity)getActivity()).loadChildFragment(fragment);
+ ((MainActivity) getActivity()).loadChildFragment(fragment);
}
@Subscribe(threadMode = ThreadMode.MAIN)
@@ -452,11 +356,11 @@ public class ItemFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if(item == null || item.getMedia() == null) {
+ if (item == null || item.getMedia() == null) {
return;
}
long mediaId = item.getMedia().getId();
- if(ArrayUtils.contains(update.mediaIds, mediaId)) {
+ if (ArrayUtils.contains(update.mediaIds, mediaId)) {
if (itemsLoaded && getActivity() != null) {
updateAppearance();
}
@@ -490,7 +394,7 @@ public class ItemFragment extends Fragment {
Context context = getContext();
if (feedItem != null && context != null) {
Timeline t = new Timeline(context, feedItem);
- webviewData = t.processShownotes(false);
+ webviewData = t.processShownotes();
}
return feedItem;
}
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 83c16a9ff..f421ed005 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -550,8 +550,9 @@ public class QueueFragment extends Fragment {
final boolean isRead = item.isPlayed();
DBWriter.markItemPlayed(FeedItem.PLAYED, false, item.getId());
DBWriter.removeQueueItem(getActivity(), true, item);
- Snackbar snackbar = Snackbar.make
- (root, getString(item.hasMedia() ? R.string.marked_as_read_label: R.string.marked_as_read_no_media_label), Snackbar.LENGTH_LONG);
+ Snackbar snackbar = Snackbar.make(root, getString(item.hasMedia()
+ ? R.string.marked_as_read_label : R.string.marked_as_read_no_media_label),
+ Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
if(!isRead) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java
index 34ea6d6e3..3059d7ad2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java
@@ -16,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadStatisticsListAdapter;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.comparator.CompareCompat;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -23,6 +24,7 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Collections;
+import java.util.List;
/**
* Displays the 'download statistics' screen
@@ -71,8 +73,8 @@ public class DownloadStatisticsFragment extends Fragment {
disposable =
Observable.fromCallable(() -> {
- DBReader.StatisticsData statisticsData = DBReader.getStatistics();
- Collections.sort(statisticsData.feeds, (item1, item2) ->
+ List<StatisticsItem> statisticsData = DBReader.getStatistics();
+ Collections.sort(statisticsData, (item1, item2) ->
CompareCompat.compareLong(item1.totalDownloadSize, item2.totalDownloadSize));
return statisticsData;
})
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 c6ae8e20c..1e51380ca 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
@@ -74,16 +74,14 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
dialog.show();
return true;
});
- findPreference(PREF_GPODNET_SYNC).
- setOnPreferenceClickListener(preference -> {
+ findPreference(PREF_GPODNET_SYNC).setOnPreferenceClickListener(preference -> {
GpodnetSyncService.sendSyncIntent(getActivity().getApplicationContext());
Toast toast = Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started,
Toast.LENGTH_SHORT);
toast.show();
return true;
});
- findPreference(PREF_GPODNET_FORCE_FULL_SYNC).
- setOnPreferenceClickListener(preference -> {
+ findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> {
GpodnetPreferences.setLastSubscriptionSyncTimestamp(0L);
GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(0L);
GpodnetPreferences.setLastSyncAttempt(false, 0);
@@ -94,17 +92,16 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
toast.show();
return true;
});
- findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
- preference -> {
+ findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(preference -> {
GpodnetPreferences.logout();
Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
toast.show();
updateGpodnetPreferenceScreen();
return true;
});
- findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
- preference -> {
- GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ findPreference(PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(
+ dialog -> updateGpodnetPreferenceScreen());
return true;
});
}
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
new file mode 100644
index 000000000..8036a7506
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
@@ -0,0 +1,283 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+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.os.Environment;
+import android.util.Log;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.FileProvider;
+import androidx.preference.PreferenceFragmentCompat;
+import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.OpmlImportActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.activity.SplashActivity;
+import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
+import de.danoeh.antennapod.asynctask.ExportWorker;
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.export.html.HtmlWriter;
+import de.danoeh.antennapod.core.export.opml.OpmlWriter;
+import de.danoeh.antennapod.core.storage.DatabaseExporter;
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.List;
+
+public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String TAG = "ImportExPrefFragment";
+ private static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ private static final String PREF_OPML_IMPORT = "prefOpmlImport";
+ private static final String PREF_HTML_EXPORT = "prefHtmlExport";
+ private static final String PREF_DATABASE_IMPORT = "prefDatabaseImport";
+ private static final String PREF_DATABASE_EXPORT = "prefDatabaseExport";
+ private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
+ private static final String CONTENT_TYPE_OPML = "text/x-opml";
+ private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
+ private static final String CONTENT_TYPE_HTML = "text/html";
+ private static final int REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH = 1;
+ private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 2;
+ private static final int REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH = 3;
+ private static final int REQUEST_CODE_RESTORE_DATABASE = 4;
+ private static final int REQUEST_CODE_BACKUP_DATABASE = 5;
+ private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup.db";
+ private Disposable disposable;
+ private ProgressDialog progressDialog;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_import_export);
+ setupStorageScreen();
+ progressDialog = new ProgressDialog(getContext());
+ progressDialog.setIndeterminate(true);
+ progressDialog.setMessage(getContext().getString(R.string.please_wait));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.import_export_pref);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void setupStorageScreen() {
+ findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ openExportPathPicker(CONTENT_TYPE_OPML, DEFAULT_OPML_OUTPUT_NAME,
+ REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH, new OpmlWriter());
+ return true;
+ }
+ );
+ findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ openExportPathPicker(CONTENT_TYPE_HTML, DEFAULT_HTML_OUTPUT_NAME,
+ REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH, new HtmlWriter());
+ return true;
+ });
+ findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
+ preference -> {
+ try {
+ Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
+ intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
+ intentGetContentAction.setType("*/*");
+ startActivityForResult(intentGetContentAction, REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ return true;
+ });
+ findPreference(PREF_DATABASE_IMPORT).setOnPreferenceClickListener(
+ preference -> {
+ importDatabase();
+ return true;
+ });
+ findPreference(PREF_DATABASE_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ exportDatabase();
+ return true;
+ });
+ }
+
+ private void exportWithWriter(ExportWriter exportWriter, final Uri uri) {
+ Context context = getActivity();
+ progressDialog.show();
+ if (uri == null) {
+ Observable<File> observable = new ExportWorker(exportWriter, getContext()).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
+ context.getString(R.string.provider_authority), output);
+ showExportSuccessDialog(output.toString(), fileUri);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ } else {
+ DocumentFileExportWorker worker = new DocumentFileExportWorker(exportWriter, context, uri);
+ disposable = worker.exportObservable()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output ->
+ showExportSuccessDialog(output.getUri().toString(), output.getUri()),
+ this::showExportErrorDialog, progressDialog::dismiss);
+ }
+ }
+
+ private void exportDatabase() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("application/x-sqlite3")
+ .putExtra(Intent.EXTRA_TITLE, DATABASE_EXPORT_FILENAME);
+
+ startActivityForResult(intent, REQUEST_CODE_BACKUP_DATABASE);
+ } else {
+ File sd = Environment.getExternalStorageDirectory();
+ File backupDB = new File(sd, DATABASE_EXPORT_FILENAME);
+ progressDialog.show();
+ disposable = Completable.fromAction(() ->
+ DatabaseExporter.exportToStream(new FileOutputStream(backupDB), getContext()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ Snackbar.make(getView(), R.string.export_success_title, Snackbar.LENGTH_LONG).show();
+ progressDialog.dismiss();
+ }, this::showExportErrorDialog);
+ }
+ }
+
+ private void importDatabase() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ startActivityForResult(intent, REQUEST_CODE_RESTORE_DATABASE);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ startActivityForResult(Intent.createChooser(intent,
+ getString(R.string.import_select_file)), REQUEST_CODE_RESTORE_DATABASE);
+ }
+ }
+
+ 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) -> {
+ Intent intent = new Intent(getContext(), SplashActivity.class);
+ ComponentName cn = intent.getComponent();
+ Intent mainIntent = Intent.makeRestartActivityTask(cn);
+ startActivity(mainIntent);
+ });
+ d.show();
+ }
+
+ private void showExportSuccessDialog(final String path, final Uri streamUri) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
+ alert.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_success_title);
+ alert.setMessage(getContext().getString(R.string.export_success_sum, path));
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = getContext().getPackageManager()
+ .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
+ });
+ alert.create().show();
+ }
+
+ private void showExportErrorDialog(final Throwable error) {
+ progressDialog.dismiss();
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
+ alert.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK || data == null) {
+ return;
+ }
+ Uri uri = data.getData();
+
+ if (requestCode == REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH) {
+ exportWithWriter(new OpmlWriter(), uri);
+ } else if (requestCode == REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH) {
+ exportWithWriter(new HtmlWriter(), uri);
+ } else if (requestCode == REQUEST_CODE_RESTORE_DATABASE) {
+ progressDialog.show();
+ disposable = Completable.fromAction(() -> DatabaseExporter.importBackup(uri, getContext()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ showDatabaseImportSuccessDialog();
+ progressDialog.dismiss();
+ }, this::showExportErrorDialog);
+ } else if (requestCode == REQUEST_CODE_BACKUP_DATABASE) {
+ progressDialog.show();
+ disposable = Completable.fromAction(() -> DatabaseExporter.exportToDocument(uri, getContext()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ Snackbar.make(getView(), R.string.export_success_title, Snackbar.LENGTH_LONG).show();
+ progressDialog.dismiss();
+ }, this::showExportErrorDialog);
+ } else if (requestCode == REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH) {
+ Intent intent = new Intent(getContext(), OpmlImportActivity.class);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+ }
+
+ private void openExportPathPicker(String contentType, String title, int requestCode, ExportWriter writer) {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(contentType)
+ .putExtra(Intent.EXTRA_TITLE, title);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, requestCode);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ exportWithWriter(writer, null);
+ }
+}
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 5fd38d663..da82d4f8c 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
@@ -103,6 +103,9 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network));
config.index(R.xml.preferences_storage)
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_storage));
+ config.index(R.xml.preferences_import_export)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_storage))
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_import_export));
config.index(R.xml.preferences_autodownload)
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network))
.addBreadcrumb(R.string.automation)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
index bed767e8e..d25dff743 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
@@ -27,6 +27,7 @@ import de.danoeh.antennapod.adapter.PlaybackStatisticsListAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.comparator.CompareCompat;
import io.reactivex.Completable;
import io.reactivex.Observable;
@@ -35,6 +36,7 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Collections;
+import java.util.List;
/**
* Displays the 'playback statistics' screen
@@ -180,13 +182,13 @@ public class PlaybackStatisticsFragment extends Fragment {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- private DBReader.StatisticsData fetchStatistics() {
- DBReader.StatisticsData statisticsData = DBReader.getStatistics();
+ private List<StatisticsItem> fetchStatistics() {
+ List<StatisticsItem> statisticsData = DBReader.getStatistics();
if (countAll) {
- Collections.sort(statisticsData.feeds, (item1, item2) ->
+ Collections.sort(statisticsData, (item1, item2) ->
CompareCompat.compareLong(item1.timePlayedCountAll, item2.timePlayedCountAll));
} else {
- Collections.sort(statisticsData.feeds, (item1, item2) ->
+ Collections.sort(statisticsData, (item1, item2) ->
CompareCompat.compareLong(item1.timePlayed, item2.timePlayed));
}
return statisticsData;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index 2c1590c47..8a0742b7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -1,62 +1,32 @@
package de.danoeh.antennapod.fragment.preferences;
import android.Manifest;
-import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
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 androidx.core.app.ActivityCompat;
-import androidx.core.content.FileProvider;
-import androidx.documentfile.provider.DocumentFile;
+import android.util.Log;
import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
import androidx.preference.PreferenceFragmentCompat;
-import android.util.Log;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
-import de.danoeh.antennapod.activity.ImportExportActivity;
-import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
-import de.danoeh.antennapod.asynctask.ExportWorker;
-import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.export.html.HtmlWriter;
-import de.danoeh.antennapod.core.export.opml.OpmlWriter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
-import io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
import java.io.File;
-import java.util.List;
public class StoragePreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "StoragePrefFragment";
- private static final String PREF_OPML_EXPORT = "prefOpmlExport";
- private static final String PREF_OPML_IMPORT = "prefOpmlImport";
- private static final String PREF_HTML_EXPORT = "prefHtmlExport";
- private static final String IMPORT_EXPORT = "importExport";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
+ private static final String PREF_IMPORT_EXPORT = "prefImportExport";
private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
- private static final int CHOOSE_OPML_EXPORT_PATH = 1;
- private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
- private static final String CONTENT_TYPE_OPML = "text/x-opml";
- private static final int CHOOSE_HTML_EXPORT_PATH = 2;
- private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
- private static final String CONTENT_TYPE_HTML = "text/html";
- private Disposable disposable;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -76,51 +46,20 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
setDataFolderText();
}
- @Override
- public void onStop() {
- super.onStop();
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
private void setupStorageScreen() {
final Activity activity = getActivity();
-
- findPreference(IMPORT_EXPORT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, ImportExportActivity.class));
- return true;
- }
- );
- findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
- preference -> {
- openOpmlExportPathPicker();
- return true;
- }
- );
- findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
- preference -> {
- openHtmlExportPathPicker();
- return true;
- });
- findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
- return true;
- });
findPreference(PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
preference -> {
- if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
- Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
showChooseDataFolderDialog();
} else {
int readPermission = ActivityCompat.checkSelfPermission(
activity, Manifest.permission.READ_EXTERNAL_STORAGE);
int writePermission = ActivityCompat.checkSelfPermission(
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
- if (readPermission == PackageManager.PERMISSION_GRANTED &&
- writePermission == PackageManager.PERMISSION_GRANTED) {
+ if (readPermission == PackageManager.PERMISSION_GRANTED
+ && writePermission == PackageManager.PERMISSION_GRANTED) {
openDirectoryChooser();
} else {
requestPermission();
@@ -129,19 +68,18 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
return true;
}
);
- findPreference(PREF_CHOOSE_DATA_DIR)
- .setOnPreferenceClickListener(
- preference -> {
- if (Build.VERSION.SDK_INT >= 19) {
- showChooseDataFolderDialog();
- } else {
- Intent intent = new Intent(activity, DirectoryChooserActivity.class);
- activity.startActivityForResult(intent,
- DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
- }
- return true;
- }
- );
+ findPreference(PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ );
findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
(preference, o) -> {
if (o instanceof String) {
@@ -158,74 +96,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
return false;
}
);
- }
-
- private boolean export(ExportWriter exportWriter) {
- return export(exportWriter, null);
- }
-
- private boolean export(ExportWriter exportWriter, final Uri uri) {
- Context context = getActivity();
- final ProgressDialog progressDialog = new ProgressDialog(context);
- progressDialog.setMessage(context.getString(R.string.exporting_label));
- progressDialog.setIndeterminate(true);
- progressDialog.show();
- if (uri == null) {
- Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
- context.getString(R.string.provider_authority), output);
- showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri);
- }, this::showExportErrorDialog, progressDialog::dismiss);
- } else {
- Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri());
- }, this::showExportErrorDialog, progressDialog::dismiss);
- }
- return true;
- }
-
- private void showExportSuccessDialog(final String message, final Uri streamUri) {
- final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- alert.setTitle(R.string.export_success_title);
- alert.setMessage(message);
- alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
- sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
- sendIntent.setType("text/plain");
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = getContext().getPackageManager()
- .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ findPreference(PREF_IMPORT_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_import_export);
+ return true;
}
- }
- getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
- });
- alert.create().show();
- }
-
- private void showExportErrorDialog(final Throwable error) {
- final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- alert.setTitle(R.string.export_error_label);
- alert.setMessage(error.getMessage());
- alert.show();
+ );
}
- @SuppressLint("NewApi")
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_OK &&
- requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ if (resultCode == Activity.RESULT_OK && requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
@@ -255,23 +135,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
ab.show();
}
}
-
- if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) {
- Uri uri = data.getData();
- export(new OpmlWriter(), uri);
- }
-
- if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) {
- Uri uri = data.getData();
- export(new HtmlWriter(), uri);
- }
}
private void setDataFolderText() {
File f = UserPreferences.getDataFolder(null);
if (f != null) {
- findPreference(PREF_CHOOSE_DATA_DIR)
- .setSummary(f.getAbsolutePath());
+ findPreference(PREF_CHOOSE_DATA_DIR).setSummary(f.getAbsolutePath());
}
}
@@ -286,50 +155,6 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
}
- private void openOpmlExportPathPicker() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
- Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType(CONTENT_TYPE_OPML)
- .putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME);
-
- // Creates an implicit intent to launch a file manager which lets
- // the user choose a specific directory to export to.
- try {
- startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH);
- return;
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "No activity found. Should never happen...");
- }
- }
-
- // If we are using a SDK lower than API 21 or the implicit intent failed
- // fallback to the legacy export process
- export(new OpmlWriter());
- }
-
- private void openHtmlExportPathPicker() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
- Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType(CONTENT_TYPE_HTML)
- .putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME);
-
- // Creates an implicit intent to launch a file manager which lets
- // the user choose a specific directory to export to.
- try {
- startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH);
- return;
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "No activity found. Should never happen...");
- }
- }
-
- // If we are using a SDK lower than API 21 or the implicit intent failed
- // fallback to the legacy export process
- export(new HtmlWriter());
- }
-
private void showChooseDataFolderDialog() {
ChooseDataFolderDialog.showDialog(
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java
new file mode 100644
index 000000000..60ef820a9
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/PagerIndicatorView.java
@@ -0,0 +1,105 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import androidx.annotation.Nullable;
+import androidx.vectordrawable.graphics.drawable.ArgbEvaluator;
+import androidx.viewpager.widget.ViewPager;
+
+public class PagerIndicatorView extends View {
+ private final Paint paint = new Paint();
+ private float position = 0;
+ private int numPages = 0;
+ private int disabledPage = -1;
+ private int circleColor = 0;
+ private int circleColorHighlight = -1;
+
+ public PagerIndicatorView(Context context) {
+ super(context);
+ setup();
+ }
+
+ public PagerIndicatorView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ setup();
+ }
+
+ public PagerIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setup();
+ }
+
+ private void setup() {
+ paint.setAntiAlias(true);
+ paint.setStyle(Paint.Style.FILL);
+
+ int[] colorAttrs = new int[] { android.R.attr.textColorSecondary };
+ TypedArray a = getContext().obtainStyledAttributes(colorAttrs);
+ circleColorHighlight = a.getColor(0, 0xffffffff);
+ circleColor = (Integer) new ArgbEvaluator().evaluate(0.8f, 0x00ffffff, circleColorHighlight);
+ a.recycle();
+ }
+
+ public void setViewPager(ViewPager pager) {
+ numPages = pager.getAdapter().getCount();
+ pager.getAdapter().registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ numPages = pager.getAdapter().getCount();
+ invalidate();
+ }
+ });
+ pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ PagerIndicatorView.this.position = position + positionOffset;
+ invalidate();
+ }
+ });
+ }
+
+ public void setDisabledPage(int disabledPage) {
+ this.disabledPage = disabledPage;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ for (int i = 0; i < numPages; i++) {
+ if ((int) Math.floor(position) == i) {
+ // This is the current dot
+ drawCircle(canvas, i, (float) (1 - (position - Math.floor(position))));
+ } else if ((int) Math.ceil(position) == i) {
+ // This is the next dot
+ drawCircle(canvas, i, (float) (position - Math.floor(position)));
+ } else {
+ drawCircle(canvas, i, 0);
+ }
+ }
+ }
+
+ private void drawCircle(Canvas canvas, int position, float frac) {
+ float circleRadiusSmall = canvas.getHeight() * 0.26f;
+ float circleRadiusBig = canvas.getHeight() * 0.35f;
+ float circleRadiusDelta = (circleRadiusBig - circleRadiusSmall);
+ float start = 0.5f * (canvas.getWidth() - numPages * 1.5f * canvas.getHeight());
+ paint.setStrokeWidth(canvas.getHeight() * 0.3f);
+
+ if (position == disabledPage) {
+ paint.setStyle(Paint.Style.STROKE);
+ } else {
+ paint.setStyle(Paint.Style.FILL_AND_STROKE);
+ }
+
+ paint.setColor((Integer) new ArgbEvaluator().evaluate(frac, circleColor, circleColorHighlight));
+ canvas.drawCircle(start + (position * 1.5f + 0.75f) * canvas.getHeight(), 0.5f * canvas.getHeight(),
+ circleRadiusSmall + frac * circleRadiusDelta, paint);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java
index c0c74c42c..ab4920119 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java
@@ -81,7 +81,7 @@ public class PieChartView extends AppCompatImageView {
}
public boolean isLargeEnoughToDisplay(int index) {
- return getPercentageOfItem(index) > 0.05;
+ return getPercentageOfItem(index) > 0.04;
}
public int getColorOfItem(int index) {
@@ -94,7 +94,6 @@ public class PieChartView extends AppCompatImageView {
private static class PieChartDrawable extends Drawable {
private static final float PADDING_DEGREES = 3f;
- private static final float STROKE_SIZE = 15f;
private PieChartData data;
private final Paint paint;
@@ -104,14 +103,16 @@ public class PieChartView extends AppCompatImageView {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
- paint.setStrokeWidth(STROKE_SIZE);
}
@Override
public void draw(@NonNull Canvas canvas) {
- float radius = getBounds().height() - STROKE_SIZE;
+ final float strokeSize = getBounds().height() / 30f;
+ paint.setStrokeWidth(strokeSize);
+
+ float radius = getBounds().height() - strokeSize;
float center = getBounds().width() / 2.f;
- RectF arcBounds = new RectF(center - radius, STROKE_SIZE, center + radius, STROKE_SIZE + radius * 2);
+ RectF arcBounds = new RectF(center - radius, strokeSize, center + radius, strokeSize + radius * 2);
float startAngle = 180;
for (int i = 0; i < data.values.length; i++) {
diff --git a/app/src/main/java/de/danoeh/antennapod/view/ShownotesWebView.java b/app/src/main/java/de/danoeh/antennapod/view/ShownotesWebView.java
new file mode 100644
index 000000000..3ea57eb5e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/ShownotesWebView.java
@@ -0,0 +1,167 @@
+package de.danoeh.antennapod.view;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import androidx.core.content.ContextCompat;
+import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.Consumer;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.playback.Timeline;
+
+public class ShownotesWebView extends WebView implements View.OnLongClickListener {
+ private static final String TAG = "ShownotesWebView";
+
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedUrl;
+ private Consumer<Integer> timecodeSelectedListener;
+ private Runnable pageFinishedListener;
+
+ public ShownotesWebView(Context context) {
+ super(context);
+ setup();
+ }
+
+ public ShownotesWebView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setup();
+ }
+
+ public ShownotesWebView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setup();
+ }
+
+ private void setup() {
+ setBackgroundColor(Color.TRANSPARENT);
+ if (!NetworkUtils.networkAvailable()) {
+ getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
+ // Use cached resources, even if they have expired
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ }
+ getSettings().setUseWideViewPort(false);
+ getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
+ getSettings().setLoadWithOverviewMode(true);
+ setOnLongClickListener(this);
+
+ setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (Timeline.isTimecodeLink(url) && timecodeSelectedListener != null) {
+ timecodeSelectedListener.accept(Timeline.getTimecodeLinkTime(selectedUrl));
+ } else {
+ IntentUtils.openInBrowser(getContext(), url);
+ }
+ return true;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ Log.d(TAG, "Page finished");
+ if (pageFinishedListener != null) {
+ pageFinishedListener.run();
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = getHitTestResult();
+ if (r != null && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
+ selectedUrl = r.getExtra();
+ showContextMenu();
+ return true;
+ }
+ selectedUrl = null;
+ return false;
+ }
+
+ public boolean onContextItemSelected(MenuItem item) {
+ if (selectedUrl == null) {
+ return false;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ IntentUtils.openInBrowser(getContext(), selectedUrl);
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getContext(), selectedUrl);
+ break;
+ case R.id.copy_url_item:
+ ClipData clipData = ClipData.newPlainText(selectedUrl, selectedUrl);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getContext()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ Snackbar.make(this, R.string.copied_url_msg, Snackbar.LENGTH_LONG).show();
+ break;
+ case R.id.go_to_position_item:
+ if (Timeline.isTimecodeLink(selectedUrl) && timecodeSelectedListener != null) {
+ timecodeSelectedListener.accept(Timeline.getTimecodeLinkTime(selectedUrl));
+ } else {
+ Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: " + selectedUrl);
+ }
+ break;
+ default:
+ selectedUrl = null;
+ return false;
+
+ }
+ selectedUrl = null;
+ return true;
+ }
+
+ @Override
+ protected void onCreateContextMenu(ContextMenu menu) {
+ super.onCreateContextMenu(menu);
+ if (selectedUrl == null) {
+ return;
+ }
+
+ if (Timeline.isTimecodeLink(selectedUrl)) {
+ menu.add(Menu.NONE, R.id.go_to_position_item, Menu.NONE, R.string.go_to_position_label);
+ menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedUrl)));
+ } else {
+ Uri uri = Uri.parse(selectedUrl);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if (IntentUtils.isCallable(getContext(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE, R.string.open_in_browser_label);
+ }
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE, R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE, R.string.share_url_label);
+ menu.setHeaderTitle(selectedUrl);
+ }
+ }
+
+ public void setTimecodeSelectedListener(Consumer<Integer> timecodeSelectedListener) {
+ this.timecodeSelectedListener = timecodeSelectedListener;
+ }
+
+ public void setPageFinishedListener(Runnable pageFinishedListener) {
+ this.pageFinishedListener = pageFinishedListener;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SwipeGestureDetector.java b/app/src/main/java/de/danoeh/antennapod/view/SwipeGestureDetector.java
deleted file mode 100644
index f4ee092df..000000000
--- a/app/src/main/java/de/danoeh/antennapod/view/SwipeGestureDetector.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package de.danoeh.antennapod.view;
-
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-public class SwipeGestureDetector extends GestureDetector.SimpleOnGestureListener {
-
- private static final String TAG = "SwipeGestureDetector";
-
- private static final int SWIPE_MIN_DISTANCE = 120;
- private static final int SWIPE_MAX_OFF_PATH = 250;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
-
- private final OnSwipeGesture callback;
-
- public SwipeGestureDetector(OnSwipeGesture callback) {
- this.callback = callback;
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- try {
- if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
- return false;
- if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
- && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
- return callback.onSwipeRightToLeft();
- } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
- && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
- return callback.onSwipeLeftToRight();
- }
- } catch (Exception e) {
- Log.d(TAG, Log.getStackTraceString(e));
- }
- return false;
- }
-
-}