Support SAF properly

This commit is contained in:
wb9688 2020-06-13 17:29:57 +02:00 committed by Stypox
parent 1e09a1768e
commit 0f75024e03
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
23 changed files with 451 additions and 311 deletions

View file

@ -2,13 +2,15 @@ package org.schabi.newpipe.settings;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
@ -29,10 +31,14 @@ import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.ZipHelper;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import us.shandian.giga.io.StoredDirectoryHelper;
import us.shandian.giga.io.StoredFileHelper;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class ContentSettingsFragment extends BasePreferenceFragment {
@ -57,24 +63,15 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
addPreferencesFromResource(R.xml.content_settings);
final Preference importDataPreference = findPreference(getString(R.string.import_data));
importDataPreference.setOnPreferenceClickListener(p -> {
final Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, false)
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_FILE);
startActivityForResult(i, REQUEST_IMPORT_PATH);
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
startActivityForResult(StoredFileHelper.getPicker(getContext()), REQUEST_IMPORT_PATH);
return true;
});
final Preference exportDataPreference = findPreference(getString(R.string.export_data));
exportDataPreference.setOnPreferenceClickListener(p -> {
final Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_DIR);
startActivityForResult(i, REQUEST_EXPORT_PATH);
exportDataPreference.setOnPreferenceClickListener((final Preference p) -> {
startActivityForResult(StoredDirectoryHelper.getPicker(getContext()),
REQUEST_EXPORT_PATH);
return true;
});
@ -89,7 +86,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
.getDefaultSharedPreferences(requireContext()).getString("app_language_key", "en");
final Preference clearCookiePref = findPreference(getString(R.string.clear_cookie_key));
clearCookiePref.setOnPreferenceClickListener(preference -> {
defaultPreferences.edit()
.putString(getString(R.string.recaptcha_cookies_key), "").apply();
@ -152,7 +148,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
@Override
public void onActivityResult(final int requestCode, final int resultCode,
@NonNull final Intent data) {
@Nullable final Intent data) {
assureCorrectAppLanguage(getContext());
super.onActivityResult(requestCode, resultCode, data);
if (DEBUG) {
@ -163,31 +159,44 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
&& resultCode == Activity.RESULT_OK && data.getData() != null) {
final String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
if (requestCode == REQUEST_EXPORT_PATH) {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(getString(R.string.finish),
(d, id) -> importDatabase(path))
.setNegativeButton(android.R.string.cancel,
(d, id) -> d.cancel());
builder.create().show();
&& resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
try {
Uri uri = data.getData();
if (FilePickerActivityHelper.isOwnFileUri(requireContext(), uri)) {
uri = Uri.fromFile(Utils.getFileForUri(uri));
}
if (requestCode == REQUEST_EXPORT_PATH) {
final StoredDirectoryHelper directory
= new StoredDirectoryHelper(requireContext(), uri, null);
final SimpleDateFormat sdf
= new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
exportDatabase(directory.createFile("NewPipeData-"
+ sdf.format(new Date()) + ".zip", "application/zip"));
} else {
final StoredFileHelper file = new StoredFileHelper(getContext(), uri,
StoredFileHelper.DEFAULT_MIME);
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(R.string.finish,
(DialogInterface d, int id) -> importDatabase(file))
.setNegativeButton(R.string.cancel,
(DialogInterface d, int id) -> d.cancel());
builder.create().show();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
}
private void exportDatabase(final String path) {
private void exportDatabase(final StoredFileHelper file) {
try {
//checkpoint before export
NewPipeDatabase.checkpoint();
final SharedPreferences preferences = PreferenceManager
.getDefaultSharedPreferences(requireContext());
manager.exportDatabase(preferences, path);
manager.exportDatabase(preferences, file);
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
} catch (final Exception e) {
@ -195,9 +204,9 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
private void importDatabase(final String filePath) {
private void importDatabase(final StoredFileHelper file) {
// check if file is supported
if (!ZipHelper.isValidZipFile(filePath)) {
if (!ZipHelper.isValidZipFile(file)) {
Toast.makeText(getContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
.show();
return;
@ -208,13 +217,13 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
throw new Exception("Could not create databases dir");
}
if (!manager.extractDb(filePath)) {
if (!manager.extractDb(file)) {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
}
//If settings file exist, ask if it should be imported.
if (manager.extractSettings(filePath)) {
if (manager.extractSettings(file)) {
final AlertDialog.Builder alert = new AlertDialog.Builder(requireContext());
alert.setTitle(R.string.import_settings);

View file

@ -1,7 +1,9 @@
package org.schabi.newpipe.settings
import android.content.SharedPreferences
import org.schabi.newpipe.streams.io.SharpOutputStream
import org.schabi.newpipe.util.ZipHelper
import us.shandian.giga.io.StoredFileHelper
import java.io.BufferedOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
@ -17,8 +19,9 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) {
* It also creates the file.
*/
@Throws(Exception::class)
fun exportDatabase(preferences: SharedPreferences, outputPath: String) {
ZipOutputStream(BufferedOutputStream(FileOutputStream(outputPath)))
fun exportDatabase(preferences: SharedPreferences, file: StoredFileHelper) {
file.create()
ZipOutputStream(BufferedOutputStream(SharpOutputStream(file.stream)))
.use { outZip ->
ZipHelper.addFileToZip(outZip, fileLocator.db.path, "newpipe.db")
@ -48,8 +51,8 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) {
return fileLocator.dbDir.exists() || fileLocator.dbDir.mkdir()
}
fun extractDb(filePath: String): Boolean {
val success = ZipHelper.extractFileFromZip(filePath, fileLocator.db.path, "newpipe.db")
fun extractDb(file: StoredFileHelper): Boolean {
val success = ZipHelper.extractFileFromZip(file, fileLocator.db.path, "newpipe.db")
if (success) {
fileLocator.dbJournal.delete()
fileLocator.dbWal.delete()
@ -59,9 +62,8 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) {
return success
}
fun extractSettings(filePath: String): Boolean {
return ZipHelper
.extractFileFromZip(filePath, fileLocator.settings.path, "newpipe.settings")
fun extractSettings(file: StoredFileHelper): Boolean {
return ZipHelper.extractFileFromZip(file, fileLocator.settings.path, "newpipe.settings")
}
fun loadSharedPreferences(preferences: SharedPreferences) {

View file

@ -8,11 +8,11 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.nononsenseapps.filepicker.Utils;
@ -57,6 +57,14 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
prefPathAudio = findPreference(downloadPathAudioPreference);
prefStorageAsk = findPreference(downloadStorageAsk);
final SwitchPreference prefUseSaf = findPreference(storageUseSafPreference);
prefUseSaf.setDefaultValue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
prefUseSaf.setChecked(NewPipeSettings.useStorageAccessFramework(ctx));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
prefUseSaf.setEnabled(false);
}
updatePreferencesSummary();
updatePathPickers(!defaultPreferences.getBoolean(downloadStorageAsk, false));
@ -177,8 +185,14 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
final int request;
if (key.equals(storageUseSafPreference)) {
Toast.makeText(getContext(), R.string.download_choose_new_path,
Toast.LENGTH_LONG).show();
if (!NewPipeSettings.useStorageAccessFramework(ctx)) {
NewPipeSettings.saveDefaultVideoDownloadDirectory(ctx);
NewPipeSettings.saveDefaultAudioDownloadDirectory(ctx);
} else {
defaultPreferences.edit().putString(downloadPathVideoPreference, null)
.putString(downloadPathAudioPreference, null).apply();
}
updatePreferencesSummary();
return true;
} else if (key.equals(downloadPathVideoPreference)) {
request = REQUEST_DOWNLOAD_VIDEO_PATH;
@ -188,22 +202,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
return super.onPreferenceTreeClick(preference);
}
final Intent i;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& NewPipeSettings.useStorageAccessFramework(ctx)) {
i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
.putExtra("android.content.extra.SHOW_ADVANCED", true)
.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| StoredDirectoryHelper.PERMISSION_FLAGS);
} else {
i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE,
FilePickerActivityHelper.MODE_DIR);
}
startActivityForResult(i, request);
startActivityForResult(StoredDirectoryHelper.getPicker(ctx), request);
return true;
}

View file

@ -2,6 +2,7 @@ package org.schabi.newpipe.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.NonNull;
@ -12,6 +13,8 @@ import org.schabi.newpipe.R;
import java.io.File;
import java.util.Set;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/*
* Created by k3b on 07.01.2016.
*
@ -65,32 +68,36 @@ public final class NewPipeSettings {
PreferenceManager.setDefaultValues(context, R.xml.update_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true);
getVideoDownloadFolder(context);
getAudioDownloadFolder(context);
saveDefaultVideoDownloadDirectory(context);
saveDefaultAudioDownloadDirectory(context);
SettingMigrations.initMigrations(context, isFirstRun);
}
private static void getVideoDownloadFolder(final Context context) {
getDir(context, R.string.download_path_video_key, Environment.DIRECTORY_MOVIES);
static void saveDefaultVideoDownloadDirectory(final Context context) {
saveDefaultDirectory(context, R.string.download_path_video_key,
Environment.DIRECTORY_MOVIES);
}
private static void getAudioDownloadFolder(final Context context) {
getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
static void saveDefaultAudioDownloadDirectory(final Context context) {
saveDefaultDirectory(context, R.string.download_path_audio_key,
Environment.DIRECTORY_MUSIC);
}
private static void getDir(final Context context, final int keyID,
final String defaultDirectoryName) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
final String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) {
return;
private static void saveDefaultDirectory(final Context context, final int keyID,
final String defaultDirectoryName) {
if (!useStorageAccessFramework(context)) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
final String downloadPath = prefs.getString(key, null);
if (!isNullOrEmpty(downloadPath)) {
return;
}
final SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
spEditor.apply();
}
final SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
spEditor.apply();
}
@NonNull
@ -103,10 +110,15 @@ public final class NewPipeSettings {
}
public static boolean useStorageAccessFramework(final Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return true;
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return false;
}
final String key = context.getString(R.string.storage_use_saf);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(key, false);
return prefs.getBoolean(key, true);
}
}

View file

@ -1,6 +1,5 @@
package org.schabi.newpipe.settings;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
@ -41,11 +40,6 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class SettingsActivity extends AppCompatActivity
implements BasePreferenceFragment.OnPreferenceStartFragmentCallback {
public static void initSettings(final Context context) {
NewPipeSettings.initSettings(context);
}
@Override
protected void onCreate(final Bundle savedInstanceBundle) {
setTheme(ThemeHelper.getSettingsThemeStyle(this));