diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index ea212e1e8..175aaca92 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -569,7 +569,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck // This part is called if with SAF preferred: // * older android version running // * save path not defined (via download settings) - // * the user as checked the "ask where to download" option + // * the user checked the "ask where to download" option if (!askForSavePath) Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); @@ -728,7 +728,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck try { if (storage.length() > 0) storage.truncate(); } catch (IOException e) { - Log.e(TAG, "failed to overwrite the file: " + storage.getUri().toString(), e); + Log.e(TAG, "failed to truncate the file: " + storage.getUri().toString(), e); showFailedDialog(R.string.overwrite_failed); return; } diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java index 4212f4cb8..db7754caf 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java @@ -35,8 +35,6 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { private String DOWNLOAD_PATH_VIDEO_PREFERENCE; private String DOWNLOAD_PATH_AUDIO_PREFERENCE; - private String DOWNLOAD_STORAGE_ASK; - private Preference prefPathVideo; private Preference prefPathAudio; private Preference prefStorageAsk; @@ -49,14 +47,14 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { DOWNLOAD_PATH_VIDEO_PREFERENCE = getString(R.string.download_path_video_key); DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_key); - DOWNLOAD_STORAGE_ASK = getString(R.string.downloads_storage_ask); + final String downloadStorageAsk = getString(R.string.downloads_storage_ask); prefPathVideo = findPreference(DOWNLOAD_PATH_VIDEO_PREFERENCE); prefPathAudio = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE); - prefStorageAsk = findPreference(DOWNLOAD_STORAGE_ASK); + prefStorageAsk = findPreference(downloadStorageAsk); updatePreferencesSummary(); - updatePathPickers(!defaultPreferences.getBoolean(DOWNLOAD_STORAGE_ASK, false)); + updatePathPickers(!defaultPreferences.getBoolean(downloadStorageAsk, false)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { prefStorageAsk.setSummary(R.string.downloads_storage_ask_summary); @@ -180,7 +178,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { } Intent i; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.hasOpenDocumentTreeSupport) { 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); @@ -221,16 +219,17 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { return; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // steps: - // 1. revoke permissions on the old save path - // 2. acquire permissions on the new save path - // 3. save the new path, if step(2) was successful - final Context ctx = getContext(); - if (ctx == null) throw new NullPointerException("getContext()"); - forgetSAFTree(ctx, defaultPreferences.getString(key, "")); + // revoke permissions on the old save path (required for SAF only) + final Context ctx = getContext(); + if (ctx == null) throw new NullPointerException("getContext()"); + forgetSAFTree(ctx, defaultPreferences.getString(key, "")); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.hasOpenDocumentTreeSupport) { + // steps to acquire the selected path: + // 1. acquire permissions on the new save path + // 2. save the new path, if step(2) was successful try { ctx.grantUriPermission(ctx.getPackageName(), uri, StoredDirectoryHelper.PERMISSION_FLAGS); diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index a0f3b6063..64f0db489 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -21,7 +21,10 @@ package org.schabi.newpipe.settings; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Build; import android.os.Environment; import android.preference.PreferenceManager; @@ -30,6 +33,7 @@ import android.support.annotation.NonNull; import org.schabi.newpipe.R; import java.io.File; +import java.util.List; /** * Helper for global settings @@ -58,6 +62,20 @@ public class NewPipeSettings { private NewPipeSettings() { } + /** + * Indicates if is possible pick a directory though the Storage Access Framework. + * {@code true} if at least one provider can handle {@link Intent#ACTION_OPEN_DOCUMENT_TREE} + * otherwise {@code false} + */ + public static boolean hasOpenDocumentTreeSupport = false; + + /** + * Indicates if is possible create a file though the Storage Access Framework. + * {@code true} if at least one provider can handle {@link Intent#ACTION_CREATE_DOCUMENT} + * otherwise {@code false} + */ + public static boolean hasCreateDocumentSupport = false; + public static void initSettings(Context context) { PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true); PreferenceManager.setDefaultValues(context, R.xml.content_settings, true); @@ -67,7 +85,14 @@ public class NewPipeSettings { PreferenceManager.setDefaultValues(context, R.xml.video_audio_settings, true); PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + hasOpenDocumentTreeSupport = testFor(context, Intent.ACTION_OPEN_DOCUMENT_TREE); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + hasCreateDocumentSupport = testFor(context, Intent.ACTION_CREATE_DOCUMENT); + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !hasOpenDocumentTreeSupport) { getVideoDownloadFolder(context); getAudioDownloadFolder(context); } @@ -100,4 +125,19 @@ public class NewPipeSettings { private static String getNewPipeChildFolderPathForDir(File dir) { return new File(dir, "NewPipe").toURI().toString(); } + + private static boolean testFor(@NonNull Context ctx, @NonNull String intentAction) { + Intent queryIntent = new Intent(intentAction); + queryIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + + List infoList = ctx.getPackageManager() + .queryIntentActivities(queryIntent, PackageManager.MATCH_DEFAULT_ONLY); + + int availableProviders = 0; + for (ResolveInfo info : infoList) { + if (info.activityInfo.exported) availableProviders++; + } + + return availableProviders > 0; + } }