Implement Storage Access Framework
* re-work finished mission database * re-work DownloadMission and bump it Serializable version * keep the classic Java IO API * SAF Tree API support on Android Lollipop or higher * add wrapper for SAF stream opening * implement Closeable in SharpStream to replace the dispose() method * do required changes for this API: ** remove any file creation logic from DownloadInitializer ** make PostProcessing Serializable and reduce the number of iterations ** update all strings.xml files ** storage helpers: StoredDirectoryHelper & StoredFileHelper ** best effort to handle any kind of SAF errors/exceptions
This commit is contained in:
parent
8d8059229f
commit
7ca7952790
62 changed files with 2439 additions and 1180 deletions
|
|
@ -2,26 +2,42 @@ package org.schabi.newpipe.settings;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nononsenseapps.filepicker.Utils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import us.shandian.giga.io.StoredDirectoryHelper;
|
||||
|
||||
public class DownloadSettingsFragment extends BasePreferenceFragment {
|
||||
private static final int REQUEST_DOWNLOAD_PATH = 0x1235;
|
||||
private static final int REQUEST_DOWNLOAD_VIDEO_PATH = 0x1235;
|
||||
private static final int REQUEST_DOWNLOAD_AUDIO_PATH = 0x1236;
|
||||
|
||||
private String DOWNLOAD_PATH_PREFERENCE;
|
||||
private String DOWNLOAD_PATH_VIDEO_PREFERENCE;
|
||||
private String DOWNLOAD_PATH_AUDIO_PREFERENCE;
|
||||
|
||||
private String DOWNLOAD_STORAGE_API;
|
||||
private String DOWNLOAD_STORAGE_API_DEFAULT;
|
||||
|
||||
private Preference prefPathVideo;
|
||||
private Preference prefPathAudio;
|
||||
|
||||
private Context ctx;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -33,16 +49,100 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
|
|||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.download_settings);
|
||||
|
||||
prefPathVideo = findPreference(DOWNLOAD_PATH_VIDEO_PREFERENCE);
|
||||
prefPathAudio = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE);
|
||||
|
||||
updatePathPickers(usingJavaIO());
|
||||
|
||||
findPreference(DOWNLOAD_STORAGE_API).setOnPreferenceChangeListener((preference, value) -> {
|
||||
boolean javaIO = DOWNLOAD_STORAGE_API_DEFAULT.equals(value);
|
||||
|
||||
if (!javaIO && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Toast.makeText(ctx, R.string.download_pick_path, Toast.LENGTH_LONG).show();
|
||||
|
||||
// forget save paths
|
||||
forgetSAFTree(DOWNLOAD_PATH_VIDEO_PREFERENCE);
|
||||
forgetSAFTree(DOWNLOAD_PATH_AUDIO_PREFERENCE);
|
||||
|
||||
defaultPreferences.edit()
|
||||
.putString(DOWNLOAD_PATH_VIDEO_PREFERENCE, "")
|
||||
.putString(DOWNLOAD_PATH_AUDIO_PREFERENCE, "")
|
||||
.apply();
|
||||
|
||||
updatePreferencesSummary();
|
||||
}
|
||||
|
||||
updatePathPickers(javaIO);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
ctx = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
ctx = null;
|
||||
findPreference(DOWNLOAD_STORAGE_API).setOnPreferenceChangeListener(null);
|
||||
}
|
||||
|
||||
private void initKeys() {
|
||||
DOWNLOAD_PATH_PREFERENCE = getString(R.string.download_path_key);
|
||||
DOWNLOAD_PATH_VIDEO_PREFERENCE = getString(R.string.download_path_video_key);
|
||||
DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key);
|
||||
DOWNLOAD_STORAGE_API = getString(R.string.downloads_storage_api);
|
||||
DOWNLOAD_STORAGE_API_DEFAULT = getString(R.string.downloads_storage_api_default);
|
||||
}
|
||||
|
||||
private void updatePreferencesSummary() {
|
||||
findPreference(DOWNLOAD_PATH_PREFERENCE).setSummary(defaultPreferences.getString(DOWNLOAD_PATH_PREFERENCE, getString(R.string.download_path_summary)));
|
||||
findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE).setSummary(defaultPreferences.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE, getString(R.string.download_path_audio_summary)));
|
||||
prefPathVideo.setSummary(
|
||||
defaultPreferences.getString(DOWNLOAD_PATH_VIDEO_PREFERENCE, getString(R.string.download_path_summary))
|
||||
);
|
||||
prefPathAudio.setSummary(
|
||||
defaultPreferences.getString(DOWNLOAD_PATH_AUDIO_PREFERENCE, getString(R.string.download_path_audio_summary))
|
||||
);
|
||||
}
|
||||
|
||||
private void updatePathPickers(boolean useJavaIO) {
|
||||
boolean enabled = useJavaIO || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
prefPathVideo.setEnabled(enabled);
|
||||
prefPathAudio.setEnabled(enabled);
|
||||
}
|
||||
|
||||
private boolean usingJavaIO() {
|
||||
return DOWNLOAD_STORAGE_API_DEFAULT.equals(
|
||||
defaultPreferences.getString(DOWNLOAD_STORAGE_API, DOWNLOAD_STORAGE_API_DEFAULT)
|
||||
);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void forgetSAFTree(String prefKey) {
|
||||
|
||||
String oldPath = defaultPreferences.getString(prefKey, "");
|
||||
|
||||
if (oldPath != null && !oldPath.isEmpty() && oldPath.charAt(0) != File.separatorChar) {
|
||||
try {
|
||||
StoredDirectoryHelper mainStorage = new StoredDirectoryHelper(ctx, Uri.parse(oldPath), null);
|
||||
if (!mainStorage.isDirect()) {
|
||||
mainStorage.revokePermissions();
|
||||
Log.i(TAG, "revokePermissions() [uri=" + oldPath + "] ¡success!");
|
||||
}
|
||||
} catch (IOException err) {
|
||||
Log.e(TAG, "Error revoking Tree uri permissions [uri=" + oldPath + "]", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showMessageDialog(@StringRes int title, @StringRes int message) {
|
||||
AlertDialog.Builder msg = new AlertDialog.Builder(ctx);
|
||||
msg.setTitle(title);
|
||||
msg.setMessage(message);
|
||||
msg.setPositiveButton(android.R.string.ok, null);
|
||||
msg.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -51,17 +151,31 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
|
|||
Log.d(TAG, "onPreferenceTreeClick() called with: preference = [" + preference + "]");
|
||||
}
|
||||
|
||||
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)
|
||||
|| preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
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);
|
||||
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) {
|
||||
startActivityForResult(i, REQUEST_DOWNLOAD_PATH);
|
||||
} else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
startActivityForResult(i, REQUEST_DOWNLOAD_AUDIO_PATH);
|
||||
String key = preference.getKey();
|
||||
|
||||
if (key.equals(DOWNLOAD_PATH_VIDEO_PREFERENCE) || key.equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
boolean safPick = !usingJavaIO();
|
||||
|
||||
int request = 0;
|
||||
if (key.equals(DOWNLOAD_PATH_VIDEO_PREFERENCE)) {
|
||||
request = REQUEST_DOWNLOAD_VIDEO_PATH;
|
||||
} else if (key.equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
|
||||
request = REQUEST_DOWNLOAD_AUDIO_PATH;
|
||||
}
|
||||
|
||||
Intent i;
|
||||
if (safPick && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
.putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_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);
|
||||
}
|
||||
|
||||
return super.onPreferenceTreeClick(preference);
|
||||
|
|
@ -71,25 +185,50 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
|
|||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
|
||||
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], " +
|
||||
"resultCode = [" + resultCode + "], data = [" + data + "]"
|
||||
);
|
||||
}
|
||||
|
||||
if ((requestCode == REQUEST_DOWNLOAD_PATH || requestCode == REQUEST_DOWNLOAD_AUDIO_PATH)
|
||||
&& resultCode == Activity.RESULT_OK && data.getData() != null) {
|
||||
String key = getString(requestCode == REQUEST_DOWNLOAD_PATH ? R.string.download_path_key : R.string.download_path_audio_key);
|
||||
String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
|
||||
if (resultCode != Activity.RESULT_OK) return;
|
||||
|
||||
defaultPreferences.edit().putString(key, path).apply();
|
||||
String key;
|
||||
if (requestCode == REQUEST_DOWNLOAD_VIDEO_PATH)
|
||||
key = DOWNLOAD_PATH_VIDEO_PREFERENCE;
|
||||
else if (requestCode == REQUEST_DOWNLOAD_AUDIO_PATH)
|
||||
key = DOWNLOAD_PATH_AUDIO_PREFERENCE;
|
||||
else
|
||||
return;
|
||||
|
||||
Uri uri = data.getData();
|
||||
if (uri == null) {
|
||||
showMessageDialog(R.string.general_error, R.string.invalid_directory);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!usingJavaIO() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// steps:
|
||||
// 1. acquire permissions on the new save path
|
||||
// 2. save the new path, if step(1) was successful
|
||||
|
||||
try {
|
||||
StoredDirectoryHelper mainStorage = new StoredDirectoryHelper(ctx, uri, null);
|
||||
mainStorage.acquirePermissions();
|
||||
Log.i(TAG, "acquirePermissions() [uri=" + uri.toString() + "] ¡success!");
|
||||
} catch (IOException err) {
|
||||
Log.e(TAG, "Error acquiring permissions on " + uri.toString());
|
||||
showMessageDialog(R.string.general_error, R.string.no_available_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
defaultPreferences.edit().putString(key, uri.toString()).apply();
|
||||
} else {
|
||||
defaultPreferences.edit().putString(key, uri.toString()).apply();
|
||||
updatePreferencesSummary();
|
||||
|
||||
File target = new File(path);
|
||||
if (!target.canWrite()) {
|
||||
AlertDialog.Builder msg = new AlertDialog.Builder(getContext());
|
||||
msg.setTitle(R.string.download_to_sdcard_error_title);
|
||||
msg.setMessage(R.string.download_to_sdcard_error_message);
|
||||
msg.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { });
|
||||
msg.show();
|
||||
}
|
||||
File target = new File(URI.create(uri.toString()));
|
||||
if (!target.canWrite())
|
||||
showMessageDialog(R.string.download_to_sdcard_error_title, R.string.download_to_sdcard_error_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,37 +70,23 @@ public class NewPipeSettings {
|
|||
getAudioDownloadFolder(context);
|
||||
}
|
||||
|
||||
public static File getVideoDownloadFolder(Context context) {
|
||||
return getDir(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
|
||||
private static void getVideoDownloadFolder(Context context) {
|
||||
getDir(context, R.string.download_path_video_key, Environment.DIRECTORY_MOVIES);
|
||||
}
|
||||
|
||||
public static String getVideoDownloadPath(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(R.string.download_path_key);
|
||||
return prefs.getString(key, Environment.DIRECTORY_MOVIES);
|
||||
private static void getAudioDownloadFolder(Context context) {
|
||||
getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
|
||||
}
|
||||
|
||||
public static File getAudioDownloadFolder(Context context) {
|
||||
return getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
|
||||
}
|
||||
|
||||
public static String getAudioDownloadPath(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(R.string.download_path_audio_key);
|
||||
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
|
||||
}
|
||||
|
||||
private static File getDir(Context context, int keyID, String defaultDirectoryName) {
|
||||
private static void getDir(Context context, int keyID, String defaultDirectoryName) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(keyID);
|
||||
String downloadPath = prefs.getString(key, null);
|
||||
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
|
||||
if ((downloadPath != null) && (!downloadPath.isEmpty())) return;
|
||||
|
||||
final File dir = getDir(defaultDirectoryName);
|
||||
SharedPreferences.Editor spEditor = prefs.edit();
|
||||
spEditor.putString(key, getNewPipeChildFolderPathForDir(dir));
|
||||
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
|
||||
spEditor.apply();
|
||||
return dir;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
@ -110,8 +96,13 @@ public class NewPipeSettings {
|
|||
|
||||
public static void resetDownloadFolders(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
prefs.edit()
|
||||
.putString(context.getString(R.string.downloads_storage_api), context.getString(R.string.downloads_storage_api_default))
|
||||
.apply();
|
||||
|
||||
resetDownloadFolder(prefs, context.getString(R.string.download_path_audio_key), Environment.DIRECTORY_MUSIC);
|
||||
resetDownloadFolder(prefs, context.getString(R.string.download_path_key), Environment.DIRECTORY_MOVIES);
|
||||
resetDownloadFolder(prefs, context.getString(R.string.download_path_video_key), Environment.DIRECTORY_MOVIES);
|
||||
}
|
||||
|
||||
private static void resetDownloadFolder(SharedPreferences prefs, String key, String defaultDirectoryName) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue