aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Hennen <TomHennen@users.noreply.github.com>2016-02-06 11:19:13 -0500
committerTom Hennen <TomHennen@users.noreply.github.com>2016-02-06 11:19:13 -0500
commit6e6a452a4d42ed7528b1d9d708bb5b171263ceb9 (patch)
treec4c3759c743992de1a31256ca724f5c6321f0b1c
parent4f70ff15633c8e508fb9ef180b8682c0209df45b (diff)
parent15a1c3e9f8d57acf8a4f60f8d382c722b22e25bc (diff)
downloadAntennaPod-6e6a452a4d42ed7528b1d9d708bb5b171263ceb9.tar.gz
Merge pull request #1639 from mfietz/issue/975-no-external-storage
No external storage / Choose Data Folder redo
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java196
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java464
-rw-r--r--app/src/main/res/layout/storage_error.xml29
-rw-r--r--app/src/main/res/menu/directory_chooser.xml16
-rw-r--r--core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 198 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.pngbin0 -> 191 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 333 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.pngbin0 -> 332 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 152 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.pngbin0 -> 149 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 220 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.pngbin0 -> 214 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 248 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 239 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 403 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.pngbin0 -> 404 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 343 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 339 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 605 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.pngbin0 -> 593 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 466 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 463 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 890 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.pngbin0 -> 890 bytes
-rw-r--r--core/src/main/res/values/attrs.xml2
-rw-r--r--core/src/main/res/values/strings.xml1
-rw-r--r--core/src/main/res/values/styles.xml8
28 files changed, 436 insertions, 289 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
index 25dc6423..62e85120 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -6,8 +6,8 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.support.v4.app.NavUtils;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -34,7 +34,8 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
* Let's the user choose a directory on the storage device. The selected folder
* will be sent back to the starting activity as an activity result.
*/
-public class DirectoryChooserActivity extends ActionBarActivity {
+public class DirectoryChooserActivity extends AppCompatActivity {
+
private static final String TAG = "DirectoryChooserActivit";
private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
@@ -250,8 +251,7 @@ public class DirectoryChooserActivity extends ActionBarActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.new_folder_item)
- .setVisible(isValidFile(selectedDir));
+ menu.findItem(R.id.new_folder_item).setVisible(isValidFile(selectedDir));
return true;
}
@@ -333,4 +333,5 @@ public class DirectoryChooserActivity extends ActionBarActivity {
private boolean isValidFile(File file) {
return file != null && file.isDirectory() && file.canRead() && file.canWrite();
}
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
index b02e82f0..e980764e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -1,30 +1,109 @@
package de.danoeh.antennapod.activity;
+import android.Manifest;
+import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Button;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.StorageUtils;
/** Is show if there is now external storage available. */
-public class StorageErrorActivity extends ActionBarActivity {
+public class StorageErrorActivity extends AppCompatActivity {
+
private static final String TAG = "StorageErrorActivity";
+ 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 = 42;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
setContentView(R.layout.storage_error);
- }
+
+ Button btnChooseDataFolder = (Button) findViewById(R.id.btnChooseDataFolder);
+ btnChooseDataFolder.setOnClickListener(v -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ openDirectoryChooser();
+ }
+ });
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
+ int readPermission = ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission != PackageManager.PERMISSION_GRANTED ||
+ writePermission != PackageManager.PERMISSION_GRANTED) {
+ requestPermission();
+ }
+ }
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(this, EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Intent intent = new Intent(this, DirectoryChooserActivity.class);
+ startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_EXTERNAL_STORAGE || grantResults.length != 2) {
+ return;
+ }
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
+ grantResults[1] != PackageManager.PERMISSION_GRANTED) {
+ new MaterialDialog.Builder(this)
+ .content(R.string.choose_data_directory_permission_rationale)
+ .positiveText(android.R.string.ok)
+ .onPositive((dialog, which) -> requestPermission())
+ .onNegative((dialog, which) -> finish())
+ .show();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (StorageUtils.storageAvailable()) {
+ leaveErrorState();
+ } else {
+ registerReceiver(mediaUpdate, new IntentFilter(Intent.ACTION_MEDIA_MOUNTED));
+ }
+ }
@Override
protected void onPause() {
@@ -32,18 +111,102 @@ public class StorageErrorActivity extends ActionBarActivity {
try {
unregisterReceiver(mediaUpdate);
} catch (IllegalArgumentException e) {
-
+ Log.e(TAG, Log.getStackTraceString(e));
}
}
- @Override
- protected void onResume() {
- super.onResume();
- if (StorageUtils.storageAvailable()) {
- leaveErrorState();
- } else {
- registerReceiver(mediaUpdate, new IntentFilter(
- Intent.ACTION_MEDIA_MOUNTED));
+ // see PreferenceController.showChooseDataFolderDialog()
+ private void showChooseDataFolderDialog() {
+ File dataFolder = UserPreferences.getDataFolder(null);
+ if(dataFolder == null) {
+ new MaterialDialog.Builder(this)
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ String dataFolderPath = dataFolder.getAbsolutePath();
+ int selectedIndex = -1;
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(this, null);
+ List<String> folders = new ArrayList<>(mediaDirs.length);
+ List<CharSequence> choices = new ArrayList<>(mediaDirs.length);
+ for(int i=0; i < mediaDirs.length; i++) {
+ if(mediaDirs[i] == null) {
+ continue;
+ }
+ String path = mediaDirs[i].getAbsolutePath();
+ folders.add(path);
+ if(dataFolderPath.equals(path)) {
+ selectedIndex = i;
+ }
+ int index = path.indexOf("Android");
+ String choice;
+ if(index >= 0) {
+ choice = path.substring(0, index);
+ } else {
+ choice = path;
+ }
+ long bytes = StorageUtils.getFreeSpaceAvailable(path);
+ String freeSpace = String.format(getString(R.string.free_space_label),
+ Converter.byteToString(bytes));
+ choices.add(Html.fromHtml("<html><small>" + choice + " [" + freeSpace + "]"
+ + "</small></html>"));
+ }
+ if(choices.size() == 0) {
+ new MaterialDialog.Builder(this)
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .title(R.string.choose_data_directory)
+ .content(R.string.choose_data_directory_message)
+ .items(choices.toArray(new CharSequence[choices.size()]))
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders.get(which);
+ UserPreferences.setDataFolder(folder);
+ leaveErrorState();
+ return true;
+ })
+ .negativeText(R.string.cancel_label)
+ .cancelable(true)
+ .build();
+ dialog.show();
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path;
+ if (dir != null) {
+ path = new File(dir);
+ } else {
+ path = getExternalFilesDir(null);
+ }
+ String message = null;
+ if(!path.exists()) {
+ message = String.format(getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ leaveErrorState();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
}
}
@@ -58,13 +221,10 @@ public class StorageErrorActivity extends ActionBarActivity {
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
if (intent.getBooleanExtra("read-only", true)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Media was mounted; Finishing activity");
+ Log.d(TAG, "Media was mounted; Finishing activity");
leaveErrorState();
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Media seemed to have been mounted read only");
+ Log.d(TAG, "Media seemed to have been mounted read only");
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index 78594476..c563d278 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -1,12 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.TimePickerDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
@@ -18,6 +19,7 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
@@ -85,6 +87,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
private CheckBoxPreference[] selectedNetworks;
+ 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;
+
public PreferenceController(PreferenceUI ui) {
this.ui = ui;
PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext())
@@ -121,54 +128,51 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
// disable expanded notification option on unsupported android versions
ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setEnabled(false);
ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- Toast toast = Toast.makeText(activity, R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
- toast.show();
- return true;
- }
+ preference -> {
+ Toast toast = Toast.makeText(activity,
+ R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
}
);
}
-
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- FlattrUtils.revokeAccessToken(activity);
- checkItemVisibility();
- return true;
- }
-
+ preference -> {
+ FlattrUtils.revokeAccessToken(activity);
+ checkItemVisibility();
+ return true;
}
);
-
ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- activity.startActivity(new Intent(
- activity, AboutActivity.class));
- return true;
- }
-
+ preference -> {
+ activity.startActivity(new Intent(activity, AboutActivity.class));
+ return true;
}
);
-
ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- new OpmlExportWorker(activity)
- .executeAsync();
-
- return true;
+ preference -> {
+ new OpmlExportWorker(activity).executeAsync();
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ 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) {
+ openDirectoryChooser();
+ } else {
+ requestPermission();
+ }
}
+ return true;
}
);
ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
@@ -185,40 +189,29 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
return true;
}
- }
- );
+ }
+ );
ui.findPreference(UserPreferences.PREF_THEME)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
-
- @Override
- public boolean onPreferenceChange(
- Preference preference, Object newValue) {
- Intent i = new Intent(activity, MainActivity.class);
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.finish();
- activity.startActivity(i);
- return true;
- }
+ (preference, newValue) -> {
+ Intent i = new Intent(activity, MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.finish();
+ activity.startActivity(i);
+ return true;
}
);
ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- showDrawerPreferencesDialog();
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
+ return true;
});
ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- showUpdateIntervalTimePreferencesDialog();
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
});
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
@@ -234,38 +227,30 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
});
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
-
- @Override
- public boolean onPreferenceChange(
- Preference preference, Object newValue) {
- if (newValue instanceof Boolean) {
- setSelectedNetworksEnabled((Boolean) newValue);
- return true;
- } else {
- return false;
- }
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
}
}
);
ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- if (o instanceof String) {
- try {
- int value = Integer.valueOf((String) o);
- if (1 <= value && value <= 50) {
- setParallelDownloadsText(value);
- return true;
- }
- } catch (NumberFormatException e) {
- return false;
+ (preference, o) -> {
+ if (o instanceof String) {
+ try {
+ int value = Integer.valueOf((String) o);
+ if (1 <= value && value <= 50) {
+ setParallelDownloadsText(value);
+ return true;
}
+ } catch (NumberFormatException e) {
+ return false;
}
- return false;
}
+ return false;
}
);
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
@@ -298,102 +283,79 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
});
ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- if (o instanceof String) {
- setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
- }
- return true;
+ (preference, o) -> {
+ if (o instanceof String) {
+ setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
}
+ return true;
}
);
ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- VariableSpeedDialog.showDialog(activity);
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
});
- ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- AuthenticationDialog dialog = new AuthenticationDialog(activity,
- R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
- null) {
-
- @Override
- protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
- GpodnetPreferences.setPassword(password);
- }
- };
- dialog.show();
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- GpodnetPreferences.logout();
- Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
- toast.show();
- updateGpodnetPreferenceScreen();
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- updateGpodnetPreferenceScreen();
- }
+ ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
+ null) {
+
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ GpodnetPreferences.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ ui.findPreference(PreferenceController.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;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ return true;
});
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
- new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
- @Override
- public void onCancelled() {
+ ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
+ .setOnPreferenceClickListener(preference -> {
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
- }
+ }
- @Override
- public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
- checkItemVisibility();
- }
- });
- return true;
- }
- });
- ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE)
- .setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- if (o instanceof String) {
- int newValue = Integer.valueOf((String) o) * 1024 * 1024;
- if (newValue != UserPreferences.getImageCacheSize()) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
- dialog.setTitle(android.R.string.dialog_alert_title);
- dialog.setMessage(R.string.pref_restart_required);
- dialog.setPositiveButton(android.R.string.ok, null);
- dialog.show();
- }
- return true;
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
+ checkItemVisibility();
}
- return false;
- }
+ });
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ int newValue = Integer.valueOf((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
}
- );
+ return true;
+ }
+ return false;
+ }
+ );
ui.findPreference("prefSendCrashReport").setOnPreferenceClickListener(preference -> {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("text/plain");
@@ -427,7 +389,12 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
- File path = new File(dir);
+ File path;
+ if(dir != null) {
+ path = new File(dir);
+ } else {
+ path = ui.getActivity().getExternalFilesDir(null);
+ }
String message = null;
final Context context= ui.getActivity().getApplicationContext();
if(!path.exists()) {
@@ -628,35 +595,31 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
List<String> prefValues = Arrays.asList(UserPreferences
.getAutodownloadSelectedNetworks());
PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
- Preference.OnPreferenceClickListener clickListener = new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- if (preference instanceof CheckBoxPreference) {
- String key = preference.getKey();
- ArrayList<String> prefValuesList = new ArrayList<String>(
- Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks())
- );
- boolean newValue = ((CheckBoxPreference) preference)
- .isChecked();
- Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
-
- int index = prefValuesList.indexOf(key);
- if (index >= 0 && newValue == false) {
- // remove network
- prefValuesList.remove(index);
- } else if (index < 0 && newValue == true) {
- prefValuesList.add(key);
- }
-
- UserPreferences.setAutodownloadSelectedNetworks(
- prefValuesList.toArray(new String[prefValuesList.size()])
- );
- return true;
- } else {
- return false;
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ ArrayList<String> prefValuesList = new ArrayList<String>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && newValue == false) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue == true) {
+ prefValuesList.add(key);
}
+
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
}
};
// create preference for each known network. attach listener and set
@@ -703,7 +666,6 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
checked[i] = true;
}
}
-
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.drawer_preferences);
builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
@@ -713,16 +675,26 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
}
});
- builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
}
+ // CHOOSE DATA FOLDER
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(ui.getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Activity activity = ui.getActivity();
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
private void showChooseDataFolderDialog() {
Context context = ui.getActivity();
File dataFolder = UserPreferences.getDataFolder(null);
@@ -786,6 +758,8 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
dialog.show();
}
+ // UPDATE TIME/INTERVAL DIALOG
+
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = ui.getActivity();
@@ -795,38 +769,34 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.callback(new MaterialDialog.ButtonCallback() {
- @Override
- public void onPositive(MaterialDialog dialog) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
- final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
- final String[] entries = getUpdateIntervalEntries(values);
- long currInterval = UserPreferences.getUpdateInterval();
- int checkedItem = -1;
- if(currInterval > 0) {
- String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
- checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
- }
- builder.setSingleChoiceItems(entries, checkedItem, (dialog1, which) -> {
- int hours = Integer.valueOf(values[which]);
- UserPreferences.setUpdateInterval(hours);
- dialog1.dismiss();
- setUpdateIntervalText();
- });
- builder.setNegativeButton(context.getString(R.string.cancel_label), null);
- builder.show();
+ builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
+ builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
}
-
- @Override
- public void onNegative(MaterialDialog dialog) {
- int hourOfDay = 7, minute = 0;
- int[] updateTime = UserPreferences.getUpdateTimeOfDay();
- if (updateTime.length == 2) {
- hourOfDay = updateTime[0];
- minute = updateTime[1];
- }
- TimePickerDialog timePickerDialog = new TimePickerDialog(context,
+ builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
+ int hours = Integer.valueOf(values[which1]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder1.show();
+ });
+ builder.onNegative((dialog, which) -> {
+ int hourOfDay = 7, minute = 0;
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (updateTime.length == 2) {
+ hourOfDay = updateTime[0];
+ minute = updateTime[1];
+ }
+ TimePickerDialog timePickerDialog = new TimePickerDialog(context,
(view, selectedHourOfDay, selectedMinute) -> {
if (view.getTag() == null) { // onTimeSet() may get called twice!
view.setTag("TAGGED");
@@ -834,17 +804,13 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
setUpdateIntervalText();
}
}, hourOfDay, minute, DateFormat.is24HourFormat(context));
- timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
- timePickerDialog.show();
- }
-
- @Override
- public void onNeutral(MaterialDialog dialog) {
- UserPreferences.setUpdateInterval(0);
- setUpdateIntervalText();
- }
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ });
+ builder.onNeutral((dialog, which) -> {
+ UserPreferences.setUpdateInterval(0);
+ setUpdateIntervalText();
});
- builder.forceStacking(true);
builder.show();
}
diff --git a/app/src/main/res/layout/storage_error.xml b/app/src/main/res/layout/storage_error.xml
index c1ee7726..8ff28b3c 100644
--- a/app/src/main/res/layout/storage_error.xml
+++ b/app/src/main/res/layout/storage_error.xml
@@ -1,25 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="16dp">
<ImageView
android:id="@+id/imageView1"
android:contentDescription="@string/external_storage_error_msg"
- android:layout_width="30dp"
- android:layout_height="30dp"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:layout_margin="16dp"
- android:src="@android:drawable/stat_notify_sdcard_usb" />
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_margin="8dp"
+ android:src="?attr/ic_sd_storage" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@+id/imageView1"
- android:layout_centerHorizontal="true"
android:layout_margin="8dp"
android:text="@string/external_storage_error_msg" />
-</RelativeLayout> \ No newline at end of file
+ <Button
+ android:id="@+id/btnChooseDataFolder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:text="@string/choose_data_directory"/>
+
+</LinearLayout>
diff --git a/app/src/main/res/menu/directory_chooser.xml b/app/src/main/res/menu/directory_chooser.xml
index 7735ffd2..3f860d63 100644
--- a/app/src/main/res/menu/directory_chooser.xml
+++ b/app/src/main/res/menu/directory_chooser.xml
@@ -1,14 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:custom="http://schemas.android.com/apk/res-auto">
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto">
+
<item
android:id="@+id/new_folder_item"
android:title="@string/create_folder_label"
- custom:showAsAction="ifRoom|withText"/>
+ android:icon="?attr/ic_create_new_folder"
+ custom:showAsAction="ifRoom|withText" />
<item
android:id="@+id/set_to_default_folder_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/set_to_default_folder"/>
-
+ android:title="@string/set_to_default_folder"
+ custom:showAsAction="collapseActionView" />
-</menu> \ No newline at end of file
+</menu>
diff --git a/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 00000000..bfe98fd0
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 00000000..a8b0ada8
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 00000000..0890b2e5
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 00000000..d9bfcfbf
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 00000000..eeed3465
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 00000000..3b9eaa82
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 00000000..f0c5ed4c
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 00000000..6595d2e1
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 00000000..82f0ad45
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 00000000..aa54623c
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 00000000..48cc9462
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 00000000..b440536f
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 00000000..802fc6fa
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 00000000..91cbc73d
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 00000000..4352dbc0
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 00000000..5e18ed27
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 00000000..baf7b6ef
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 00000000..aa103bfd
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 00000000..cf03bc3f
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 00000000..2996619c
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 0eb4d581..2d3379d9 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -50,6 +50,8 @@
<attr name="ic_check_box_outline" format="reference"/>
<attr name="ic_indeterminate_check_box" format="reference"/>
<attr name="ic_sort" format="reference"/>
+ <attr name="ic_sd_storage" format="reference"/>
+ <attr name="ic_create_new_folder" format="reference"/>
<!-- Used in itemdescription -->
<attr name="non_transparent_background" format="reference"/>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 7f259947..912a60f5 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -485,6 +485,7 @@
<string name="create_folder_label">Create folder</string>
<string name="choose_data_directory">Choose Data Folder</string>
<string name="choose_data_directory_message">Please choose the base of your data folder. AntennaPod will create the appropriate sub-directories.</string>
+ <string name="choose_data_directory_permission_rationale">Access to external storage is required to change the data folder</string>
<string name="create_folder_msg">Create new folder with name "%1$s"?</string>
<string name="create_folder_success">Created new folder</string>
<string name="create_folder_error_no_write_access">Cannot write to this folder</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index d3718a46..c4a731a5 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -57,6 +57,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark" parent="Theme.AppCompat">
@@ -114,6 +116,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
</style>
<style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
@@ -173,6 +177,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
@@ -231,6 +237,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
</style>
<style name="Theme.AntennaPod.VideoPlayer" parent="@style/Theme.AntennaPod.Dark">