summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/AndroidManifest.xml14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java218
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java31
-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/asynctask/OpmlImportWorker.java118
-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/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/StoragePreferencesFragment.java227
-rw-r--r--app/src/main/res/layout/import_export_activity.xml34
-rw-r--r--app/src/main/res/layout/opml_import.xml65
-rw-r--r--app/src/main/res/layout/time_dialog.xml122
-rw-r--r--app/src/main/res/xml/preferences_import_export.xml36
-rw-r--r--app/src/main/res/xml/preferences_storage.xml21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java61
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java95
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java4
-rw-r--r--core/src/main/res/values/strings.xml38
23 files changed, 814 insertions, 971 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ad68fcfe3..0fb753b0c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -141,19 +141,7 @@
<activity android:name=".activity.StorageErrorActivity">
</activity>
<activity
- android:name=".activity.ImportExportActivity"
- android:label="@string/import_export">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
- </activity>
- <activity
- android:name=".activity.OpmlImportFromPathActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:label="@string/opml_import_label">
- </activity>
- <activity
- android:name=".activity.OpmlImportFromIntentActivity"
+ android:name=".activity.OpmlImportActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/opml_import_label">
<intent-filter>
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/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 538ed1231..021ff774d 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;
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/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/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/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
new file mode 100644
index 000000000..9a09d55b0
--- /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())
+ .setNeutralButton(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/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index 5ce852ed2..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, 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(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/res/layout/import_export_activity.xml b/app/src/main/res/layout/import_export_activity.xml
deleted file mode 100644
index 97ff34e41..000000000
--- a/app/src/main/res/layout/import_export_activity.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:id="@+id/import_export_layout"
- android:padding="8dp"
- android:clipToPadding="false">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/import_export_warning"
- android:gravity="center_horizontal"/>
-
- <Button
- android:text="@string/label_export"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/button_export"
- android:layout_marginTop="24dp"/>
-
- <Button
- android:text="@string/label_import"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/button_import"/>
- </LinearLayout>
-</ScrollView>
diff --git a/app/src/main/res/layout/opml_import.xml b/app/src/main/res/layout/opml_import.xml
deleted file mode 100644
index 9b2036228..000000000
--- a/app/src/main/res/layout/opml_import.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingBottom="8dp"
- tools:background="@android:color/darker_gray">
-
- <TextView
- android:id="@+id/txtvHeadingExplanation"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
-
- <TextView
- android:id="@+id/txtvExplanation"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/opml_import_explanation_1"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"
- tools:background="@android:color/holo_green_dark" />
-
- <Button
- android:id="@+id/butChooseFileFromFilesystem"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="8dp"
- android:text="@string/choose_file_from_filesystem" />
-
- <View
- android:id="@+id/divider"
- android:layout_width="fill_parent"
- android:layout_height="1dp"
- android:layout_margin="16dp"
- android:background="?android:attr/listDivider"/>
-
- <TextView
- android:id="@+id/txtvHeadingExplanationOpenWith"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
-
- <TextView
- android:id="@+id/txtvExplanationOpenWith"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/opml_import_explanation_3"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"
- tools:background="@android:color/holo_green_dark" />
-
- </LinearLayout>
-</ScrollView>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index b3742c20c..8ada0d6cf 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,67 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="16dp">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/etxtTime"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_margin="8dp"
- android:ems="2"
- android:hint="@string/enter_time_here_label"
- android:inputType="number"
- android:maxLength="2" >
-
- <requestFocus />
- </EditText>
-
- <Spinner
- android:id="@+id/spTimeUnit"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp" />
+ android:id="@+id/timeSetup"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/etxtTime"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_margin="8dp"
+ android:ems="2"
+ android:inputType="number"
+ android:maxLength="3"/>
+
+ <Spinner
+ android:id="@+id/spTimeUnit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"/>
+ </LinearLayout>
+
+ <Button
+ android:text="@string/set_sleeptimer_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/setSleeptimerButton"/>
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/timeDisplay"
+ android:orientation="vertical"
+ android:visibility="gone">
<TextView
- android:layout_width="wrap_content"
+ android:text="00:00:00"
+ android:layout_gravity="center"
+ android:textSize="32sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/time"/>
+
+ <Button
+ android:text="@string/disable_sleeptimer_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/disableSleeptimerButton"/>
+
+ </LinearLayout>
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/timer_about_to_expire_label"
- android:textSize="16sp" />
+ android:orientation="vertical"
+ android:layout_marginTop="8dp">
<CheckBox
- android:id="@+id/cbShakeToReset"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/shake_to_reset_label" />
+ android:id="@+id/cbShakeToReset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shake_to_reset_label"/>
<CheckBox
- android:id="@+id/cbVibrate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/timer_vibration_label" />
+ android:id="@+id/cbVibrate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/timer_vibration_label"/>
<CheckBox
- android:id="@+id/chAutoEnable"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/auto_enable_label" />
+ android:id="@+id/chAutoEnable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/auto_enable_label"/>
</LinearLayout>
diff --git a/app/src/main/res/xml/preferences_import_export.xml b/app/src/main/res/xml/preferences_import_export.xml
new file mode 100644
index 000000000..12e27236d
--- /dev/null
+++ b/app/src/main/res/xml/preferences_import_export.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch">
+
+ <PreferenceCategory android:title="@string/database">
+ <Preference
+ android:key="prefDatabaseExport"
+ search:keywords="@string/import_export_search_keywords"
+ android:title="@string/database_export_label"
+ android:summary="@string/database_export_summary"/>
+ <Preference
+ android:key="prefDatabaseImport"
+ search:keywords="@string/import_export_search_keywords"
+ android:title="@string/database_import_label"
+ android:summary="@string/database_import_summary"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/opml">
+ <Preference
+ android:key="prefOpmlExport"
+ android:title="@string/opml_export_label"
+ android:summary="@string/opml_export_summary"/>
+ <Preference
+ android:key="prefOpmlImport"
+ android:title="@string/opml_import_label"
+ android:summary="@string/opml_import_summary"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/html">
+ <Preference
+ android:key="prefHtmlExport"
+ android:title="@string/html_export_label"
+ android:summary="@string/html_export_summary"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_storage.xml b/app/src/main/res/xml/preferences_storage.xml
index 9f394ad12..18e2abb31 100644
--- a/app/src/main/res/xml/preferences_storage.xml
+++ b/app/src/main/res/xml/preferences_storage.xml
@@ -31,20 +31,9 @@
android:key="prefDeleteRemovesFromQueue"
android:summary="@string/pref_delete_removes_from_queue_sum"
android:title="@string/pref_delete_removes_from_queue_title"/>
-
- <PreferenceCategory android:title="@string/import_export_pref">
- <Preference
- android:key="prefOpmlExport"
- android:title="@string/opml_export_label"/>
- <Preference
- android:key="prefOpmlImport"
- android:title="@string/opml_import_label"/>
- <Preference
- android:key="prefHtmlExport"
- android:title="@string/html_export_label"/>
- <Preference
- android:key="importExport"
- search:keywords="@string/import_export_search_keywords"
- android:title="@string/import_export"/>
- </PreferenceCategory>
+ <Preference
+ android:title="@string/import_export_pref"
+ android:summary="@string/import_export_summary"
+ android:key="prefImportExport"
+ search:ignore="true"/>
</PreferenceScreen>
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 2fb37cc05..edcaacefc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -745,10 +745,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
setupPositionUpdater();
stateManager.validStartCommandWasReceived();
// set sleep timer if auto-enabled
- if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
- SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
+ if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING
+ && SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
setSleepTimer(SleepTimerPreferences.timerMillis(), SleepTimerPreferences.shakeToReset(),
SleepTimerPreferences.vibrate());
+ EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
+ PlaybackService.this::disableSleepTimer));
}
break;
@@ -1007,17 +1009,14 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
public void setSleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
- Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
+ Log.d(TAG, "Setting sleep timer to " + waitingTime + " milliseconds");
taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate);
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
- this::disableSleepTimer));
}
public void disableSleepTimer() {
taskManager.disableSleepTimer();
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_disabled_label)));
}
private void sendNotificationBroadcast(int type, int code) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index 736cf8cf2..62eda415e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -397,41 +397,42 @@ public class PlaybackServiceTaskManager {
while (timeLeft > 0) {
try {
Thread.sleep(UPDATE_INTERVAL);
- long now = System.currentTimeMillis();
- timeLeft -= now - lastTick;
- lastTick = now;
-
- if(timeLeft < NOTIFICATION_THRESHOLD && !notifiedAlmostExpired) {
- Log.d(TAG, "Sleep timer is about to expire");
- if(vibrate) {
- Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if(v != null) {
- v.vibrate(500);
- }
- }
- if(shakeListener == null && shakeToReset) {
- shakeListener = new ShakeListener(context, this);
- }
- postCallback(callback::onSleepTimerAlmostExpired);
- notifiedAlmostExpired = true;
- }
- if (timeLeft <= 0) {
- Log.d(TAG, "Sleep timer expired");
- if(shakeListener != null) {
- shakeListener.pause();
- shakeListener = null;
- }
- if (!Thread.currentThread().isInterrupted()) {
- postCallback(callback::onSleepTimerExpired);
- } else {
- Log.d(TAG, "Sleep timer interrupted");
- }
- }
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted while waiting");
e.printStackTrace();
break;
}
+
+ long now = System.currentTimeMillis();
+ timeLeft -= now - lastTick;
+ lastTick = now;
+
+ if (timeLeft < NOTIFICATION_THRESHOLD && !notifiedAlmostExpired) {
+ Log.d(TAG, "Sleep timer is about to expire");
+ if (vibrate) {
+ Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (v != null) {
+ v.vibrate(500);
+ }
+ }
+ if (shakeListener == null && shakeToReset) {
+ shakeListener = new ShakeListener(context, this);
+ }
+ postCallback(callback::onSleepTimerAlmostExpired);
+ notifiedAlmostExpired = true;
+ }
+ if (timeLeft <= 0) {
+ Log.d(TAG, "Sleep timer expired");
+ if (shakeListener != null) {
+ shakeListener.pause();
+ shakeListener = null;
+ }
+ if (!Thread.currentThread().isInterrupted()) {
+ postCallback(callback::onSleepTimerExpired);
+ } else {
+ Log.d(TAG, "Sleep timer interrupted");
+ }
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
new file mode 100644
index 000000000..af3d1206c
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
@@ -0,0 +1,95 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import de.danoeh.antennapod.core.R;
+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;
+
+public class DatabaseExporter {
+ private static final String TAG = "DatabaseExporter";
+ private static final byte[] SQLITE3_MAGIC = "SQLite format 3\0".getBytes();
+
+ public static boolean validateDB(Uri inputUri, Context context) throws IOException {
+ try (InputStream inputStream = context.getContentResolver().openInputStream(inputUri)) {
+ byte[] magicBuf = new byte[SQLITE3_MAGIC.length];
+ if (inputStream.read(magicBuf) == magicBuf.length) {
+ return Arrays.equals(SQLITE3_MAGIC, magicBuf);
+ }
+ }
+ return false;
+ }
+
+ public static void exportToDocument(Uri uri, Context context) throws IOException {
+ ParcelFileDescriptor pfd = null;
+ FileOutputStream fileOutputStream = null;
+ try {
+ pfd = context.getContentResolver().openFileDescriptor(uri, "w");
+ fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
+ exportToStream(fileOutputStream, context);
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(fileOutputStream);
+
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to close ParcelFileDescriptor");
+ }
+ }
+ }
+ }
+
+ public static void exportToStream(FileOutputStream outFileStream, Context context) throws IOException {
+ FileChannel src = null;
+ FileChannel dst = null;
+ try {
+ File currentDB = context.getDatabasePath(PodDBAdapter.DATABASE_NAME);
+
+ if (currentDB.exists()) {
+ src = new FileInputStream(currentDB).getChannel();
+ dst = outFileStream.getChannel();
+ dst.transferFrom(src, 0, src.size());
+ } else {
+ throw new IOException("Can not access current database");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(src);
+ IOUtils.closeQuietly(dst);
+ }
+ }
+
+ public static void importBackup(Uri inputUri, Context context) throws IOException {
+ InputStream inputStream = null;
+ try {
+ if (!validateDB(inputUri, context)) {
+ throw new IOException(context.getString(R.string.import_bad_file));
+ }
+
+ File currentDB = context.getDatabasePath(PodDBAdapter.DATABASE_NAME);
+ inputStream = context.getContentResolver().openInputStream(inputUri);
+ FileUtils.copyInputStreamToFile(inputStream, currentDB);
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index e7f6ad4f1..cb22fbcc9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -628,10 +628,6 @@ public class PlaybackController {
return playbackService != null && playbackService.sleepTimerActive();
}
- public boolean sleepTimerNotActive() {
- return playbackService != null && !playbackService.sleepTimerActive();
- }
-
public void disableSleepTimer() {
if (playbackService != null) {
playbackService.disableSleepTimer();
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index f9e38ded9..d87afcd09 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -313,6 +313,7 @@
<string name="clear_queue_confirmation_msg">Please confirm that you want to clear the queue of ALL of the episodes in it</string>
<string name="sort_old_to_new">Old to new</string>
<string name="sort_new_to_old">New to old</string>
+ <string name="time_left_label">Time left:\u0020</string>
<!-- Variable Speed -->
<string name="download_plugin_label">Download Plugin</string>
@@ -545,34 +546,39 @@
<string name="found_in_title_label">Found in title</string>
<string name="no_results_for_query">No results were found for \"%1$s\"</string>
- <!-- OPML import and export -->
- <string name="opml_import_option">Option %1$d</string>
- <string name="opml_import_explanation_1">Choose a specific file path from the local filesystem.</string>
- <string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string>
+ <!-- import and export -->
+ <string name="import_export_summary">Move subscriptions and queue to another device</string>
+ <string name="database">Database</string>
+ <string name="opml">OPML</string>
+ <string name="html">HTML</string>
+ <string name="html_export_summary">Show your subscriptions to a friend</string>
+ <string name="opml_export_summary">Transfer your subscriptions to another podcast app</string>
+ <string name="opml_import_summary">Import your subscriptions from another podcast app</string>
+ <string name="database_export_summary">Transfer subscriptions, listened episodes and queue to AntennaPod on another device</string>
+ <string name="database_import_summary">Import AntennaPod database from another device</string>
<string name="opml_import_label">OPML Import</string>
- <string name="reading_opml_label">Reading OPML file</string>
<string name="opml_reader_error">An error has occurred while reading the OPML document:</string>
<string name="opml_import_error_no_file">No file selected!</string>
<string name="select_all_label">Select all</string>
<string name="deselect_all_label">Deselect all</string>
- <string name="choose_file_from_filesystem">From local filesystem</string>
<string name="opml_export_label">OPML export</string>
<string name="html_export_label">HTML export</string>
- <string name="exporting_label">Exporting&#8230;</string>
+ <string name="database_export_label">Database export</string>
+ <string name="database_import_label">Database import</string>
+ <string name="please_wait">Please wait&#8230;</string>
<string name="export_error_label">Export error</string>
<string name="export_success_title">Export successful</string>
<string name="export_success_sum">The exported file was written to:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Access to external storage is required to read the OPML file</string>
+ <string name="import_select_file">Select file to import</string>
+ <string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string>
<!-- Sleep timer -->
<string name="set_sleeptimer_label">Set sleep timer</string>
<string name="disable_sleeptimer_label">Disable sleep timer</string>
- <string name="enter_time_here_label">Enter time</string>
<string name="sleep_timer_label">Sleep timer</string>
- <string name="time_left_label">Time left:\u0020</string>
<string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string>
- <string name="timer_about_to_expire_label"><b>When timer is about to expire:</b></string>
- <string name="shake_to_reset_label">Shake to reset timer</string>
+ <string name="shake_to_reset_label">Shake to reset</string>
<string name="timer_vibration_label">Vibrate</string>
<string name="time_seconds">seconds</string>
<string name="time_minutes">minutes</string>
@@ -591,7 +597,6 @@
</plurals>
<string name="auto_enable_label">Auto-enable</string>
<string name="sleep_timer_enabled_label">Sleep timer enabled</string>
- <string name="sleep_timer_disabled_label">Sleep timer disabled</string>
<!-- gpodder.net -->
<string name="gpodnet_taglist_header">CATEGORIES</string>
@@ -756,15 +761,6 @@
<!-- Subscriptions fragment -->
<string name="subscription_num_columns">Number of columns</string>
- <!-- Database import/export -->
- <string name="import_export">Database import/export</string>
- <string name="import_export_warning">This experimental function can be used to transfer your subscriptions and played episodes to another device.\n\nExported databases can only be imported when using the same version of AntennaPod. Otherwise, this function will lead to unexpected behavior.\n\nAfter importing, episodes might be displayed as downloaded even though they are not. Just press the play button of the episodes to make AntennaPod detect this.</string>
- <string name="label_import">Import</string>
- <string name="label_export">Export</string>
- <string name="import_select_file">Select file to import</string>
- <string name="export_ok">Export successful.</string>
- <string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string>
-
<!-- Casting -->
<string name="cast_media_route_menu_title">Play on&#8230;</string>
<string name="cast_disconnect_label">Disconnect the cast session</string>