From 652184506b238704f0841d873e25ded9e98552d8 Mon Sep 17 00:00:00 2001 From: kapodamy Date: Wed, 14 Aug 2019 15:30:34 -0300 Subject: [PATCH 1/5] check for Storage Access Framework features * creating files though saf * picking folder though saf --- .../newpipe/download/DownloadDialog.java | 4 +- .../settings/DownloadSettingsFragment.java | 27 ++++++------ .../newpipe/settings/NewPipeSettings.java | 42 ++++++++++++++++++- 3 files changed, 56 insertions(+), 17 deletions(-) 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; + } } From 950cf714d9e71f286f153027e3cb5b0b1c964d59 Mon Sep 17 00:00:00 2001 From: kapodamy Date: Wed, 14 Aug 2019 22:15:42 -0300 Subject: [PATCH 2/5] use legacy file picker in those cases where saf is not available --- .../newpipe/download/DownloadDialog.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) 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 175aaca92..6814a018c 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.download; import android.app.Activity; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; @@ -45,6 +46,8 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.settings.NewPipeSettings; +import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.FilenameUtils; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.SecondaryStreamHelper; @@ -52,7 +55,9 @@ import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; import org.schabi.newpipe.util.ThemeHelper; +import java.io.File; import java.io.IOException; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -72,7 +77,7 @@ import us.shandian.giga.service.MissionState; public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { private static final String TAG = "DialogFragment"; private static final boolean DEBUG = MainActivity.DEBUG; - private static final int REQUEST_DOWNLOAD_PATH_SAF = 0x1230; + private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230; @State protected StreamInfo currentInfo; @@ -311,12 +316,18 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_DOWNLOAD_PATH_SAF && resultCode == Activity.RESULT_OK) { + if (requestCode == REQUEST_DOWNLOAD_SAVE_AS && resultCode == Activity.RESULT_OK) { if (data.getData() == null) { showFailedDialog(R.string.general_error); return; } + if (ContentResolver.SCHEME_FILE.equals(data.getData().getScheme())) { + File file = new File(URI.create(data.getData().toString())); + checkSelectedDownload(null, data.getData(), file.getName(), StoredFileHelper.DEFAULT_MIME); + return; + } + DocumentFile docFile = DocumentFile.fromSingleUri(context, data.getData()); if (docFile == null) { showFailedDialog(R.string.general_error); @@ -574,7 +585,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck if (!askForSavePath) Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); - StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_PATH_SAF, filename, mime); + if (NewPipeSettings.hasCreateDocumentSupport) + StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_SAVE_AS, filename, mime); + else + startActivityForResult(FilePickerActivityHelper.chooseFileToSave(context, filename), REQUEST_DOWNLOAD_SAVE_AS); + return; } From dee3a18ea804384f48724e4388d8d9820a56dcee Mon Sep 17 00:00:00 2001 From: kapodamy Date: Wed, 14 Aug 2019 23:00:11 -0300 Subject: [PATCH 3/5] misc changes * restore permission request popup previously removed in #2486 * use legacy file picker in cases where saf file picker is not available * fix missing file check logic in prepareSelectedDownload method (DownloadDialog.java) --- .../java/org/schabi/newpipe/MainActivity.java | 12 ++++++ .../org/schabi/newpipe/RouterActivity.java | 36 ++++++++++------- .../newpipe/download/DownloadDialog.java | 39 +++++++++++++++---- .../fragments/detail/VideoDetailFragment.java | 5 ++- .../newpipe/settings/NewPipeSettings.java | 17 +++++--- .../schabi/newpipe/util/NavigationHelper.java | 3 ++ .../schabi/newpipe/util/PermissionHelper.java | 6 ++- 7 files changed, 88 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index bf538cc65..a9f2e9622 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -60,6 +60,7 @@ import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.ThemeHelper; @@ -421,6 +422,17 @@ public class MainActivity extends AppCompatActivity { return; } } + switch (requestCode) { + case PermissionHelper.DOWNLOADS_REQUEST_CODE: + NavigationHelper.openDownloads(this); + break; + case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE: + Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); + if (fragment instanceof VideoDetailFragment) { + ((VideoDetailFragment) fragment).openDownloadDialog(); + } + break; + } } /** diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 88f6bdb2b..89de0d0ef 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -74,10 +74,13 @@ import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr; */ public class RouterActivity extends AppCompatActivity { - @State protected int currentServiceId = -1; + @State + protected int currentServiceId = -1; private StreamingService currentService; - @State protected LinkType currentLinkType; - @State protected int selectedRadioPosition = -1; + @State + protected LinkType currentLinkType; + @State + protected int selectedRadioPosition = -1; protected int selectedPreviously = -1; protected String currentUrl; @@ -257,7 +260,7 @@ public class RouterActivity extends AppCompatActivity { .setNegativeButton(R.string.just_once, dialogButtonsClickListener) .setPositiveButton(R.string.always, dialogButtonsClickListener) .setOnDismissListener((dialog) -> { - if(!selectionIsDownload) finish(); + if (!selectionIsDownload) finish(); }) .create(); @@ -358,13 +361,13 @@ public class RouterActivity extends AppCompatActivity { positiveButton.setEnabled(state); } - private void handleText(){ + private void handleText() { String searchString = getIntent().getStringExtra(Intent.EXTRA_TEXT); int serviceId = getIntent().getIntExtra(Constants.KEY_SERVICE_ID, 0); Intent intent = new Intent(getThemeWrapperContext(), MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); - NavigationHelper.openSearch(getThemeWrapperContext(),serviceId,searchString); + NavigationHelper.openSearch(getThemeWrapperContext(), serviceId, searchString); } private void handleChoice(final String selectedChoiceKey) { @@ -382,8 +385,10 @@ public class RouterActivity extends AppCompatActivity { } if (selectedChoiceKey.equals(getString(R.string.download_key))) { - selectionIsDownload = true; - openDownloadDialog(); + if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { + selectionIsDownload = true; + openDownloadDialog(); + } return; } @@ -395,7 +400,7 @@ public class RouterActivity extends AppCompatActivity { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(intent -> { - if(!internalRoute){ + if (!internalRoute) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); } @@ -445,17 +450,21 @@ public class RouterActivity extends AppCompatActivity { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - for (int i: grantResults){ - if (i == PackageManager.PERMISSION_DENIED){ + for (int i : grantResults) { + if (i == PackageManager.PERMISSION_DENIED) { finish(); return; } } + if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) { + openDownloadDialog(); + } } private static class AdapterChoiceItem { final String description, key; - @DrawableRes final int icon; + @DrawableRes + final int icon; AdapterChoiceItem(String key, String description, int icon) { this.description = description; @@ -553,7 +562,8 @@ public class RouterActivity extends AppCompatActivity { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false); - boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);; + boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false); + ; PlayQueue playQueue; String playerChoice = choice.playerChoice; 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 6814a018c..a9afc93d5 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -2,13 +2,13 @@ package org.schabi.newpipe.download; import android.app.Activity; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; +import android.os.Environment; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.annotation.IdRes; @@ -34,6 +34,8 @@ import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; +import com.nononsenseapps.filepicker.Utils; + import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; @@ -50,6 +52,7 @@ import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.FilenameUtils; import org.schabi.newpipe.util.ListHelper; +import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.SecondaryStreamHelper; import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; @@ -57,7 +60,6 @@ import org.schabi.newpipe.util.ThemeHelper; import java.io.File; import java.io.IOException; -import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -178,6 +180,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); + if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { + getDialog().dismiss(); + return; + } + context = getContext(); setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); @@ -322,9 +329,9 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck return; } - if (ContentResolver.SCHEME_FILE.equals(data.getData().getScheme())) { - File file = new File(URI.create(data.getData().toString())); - checkSelectedDownload(null, data.getData(), file.getName(), StoredFileHelper.DEFAULT_MIME); + if (data.getData().getAuthority() != null && data.getData().getAuthority().startsWith(context.getPackageName())) { + File file = Utils.getFileForUri(data.getData()); + checkSelectedDownload(null, Uri.fromFile(file), file.getName(), StoredFileHelper.DEFAULT_MIME); return; } @@ -585,10 +592,21 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck if (!askForSavePath) Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); - if (NewPipeSettings.hasCreateDocumentSupport) + if (NewPipeSettings.hasCreateDocumentSupport) { StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_SAVE_AS, filename, mime); - else - startActivityForResult(FilePickerActivityHelper.chooseFileToSave(context, filename), REQUEST_DOWNLOAD_SAVE_AS); + } else { + File initialSavePath; + if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.audio_button) + initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MUSIC); + else + initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MOVIES); + + initialSavePath = new File(initialSavePath, filename); + startActivityForResult( + FilePickerActivityHelper.chooseFileToSave(context, initialSavePath.getAbsolutePath()), + REQUEST_DOWNLOAD_SAVE_AS + ); + } return; } @@ -639,6 +657,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck // This part is called if: // * using SAF on older android version // * save path not defined + // * if the file exists overwrite it, is not necessary ask + if (!storage.existsAsFile() && !storage.create()) { + showFailedDialog(R.string.error_file_creation); + return; + } continueSelectedDownload(storage); return; } else if (targetFile == null) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 9d8481251..d0ed04279 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -384,7 +384,10 @@ public class VideoDetailFragment } break; case R.id.detail_controls_download: - this.openDownloadDialog(); + if (PermissionHelper.checkStoragePermissions(activity, + PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { + this.openDownloadDialog(); + } break; case R.id.detail_uploader_root_layout: if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) { 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 64f0db489..cda3cad10 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -86,10 +86,10 @@ public class NewPipeSettings { PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - hasOpenDocumentTreeSupport = testFor(context, Intent.ACTION_OPEN_DOCUMENT_TREE); + hasOpenDocumentTreeSupport = testFor(context, Intent.ACTION_OPEN_DOCUMENT_TREE, false); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - hasCreateDocumentSupport = testFor(context, Intent.ACTION_CREATE_DOCUMENT); + hasCreateDocumentSupport = testFor(context, Intent.ACTION_CREATE_DOCUMENT, true); } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !hasOpenDocumentTreeSupport) { @@ -118,7 +118,7 @@ public class NewPipeSettings { } @NonNull - private static File getDir(String defaultDirectoryName) { + public static File getDir(String defaultDirectoryName) { return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName); } @@ -126,9 +126,14 @@ public class NewPipeSettings { 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); + private static boolean testFor(@NonNull Context ctx, @NonNull String intentAction, boolean isFile) { + Intent queryIntent = new Intent(intentAction) + .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + + if (isFile) { + queryIntent.setType("*/*"); + queryIntent.addCategory(Intent.CATEGORY_OPENABLE); + } List infoList = ctx.getPackageManager() .queryIntentActivities(queryIntent, PackageManager.MATCH_DEFAULT_ONLY); diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index bb176166b..89c4b33fe 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -446,6 +446,9 @@ public class NavigationHelper { } public static boolean openDownloads(Activity activity) { + if (!PermissionHelper.checkStoragePermissions(activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { + return false; + } Intent intent = new Intent(activity, DownloadActivity.class); activity.startActivity(intent); return true; diff --git a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java index 1367b895c..2fe5f95e4 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -18,10 +18,12 @@ import android.widget.Toast; import org.schabi.newpipe.R; public class PermissionHelper { + public static final int DOWNLOAD_DIALOG_REQUEST_CODE = 778; + public static final int DOWNLOADS_REQUEST_CODE = 777; public static boolean checkStoragePermissions(Activity activity, int requestCode) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - if(!checkReadStoragePermissions(activity, requestCode)) return false; + if (!checkReadStoragePermissions(activity, requestCode)) return false; } return checkWriteStoragePermissions(activity, requestCode); } @@ -89,7 +91,7 @@ public class PermissionHelper { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); return false; - }else return true; + } else return true; } public static boolean isPopupEnabled(Context context) { From 8f13a7ec973c7f5180b03ef7867dc608f726cc8a Mon Sep 17 00:00:00 2001 From: kapodamy Date: Thu, 15 Aug 2019 21:48:07 -0300 Subject: [PATCH 4/5] check if the if the content provider is disabled (the app itself) --- .../java/org/schabi/newpipe/settings/NewPipeSettings.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 cda3cad10..6f26534fb 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -140,7 +140,9 @@ public class NewPipeSettings { int availableProviders = 0; for (ResolveInfo info : infoList) { - if (info.activityInfo.exported) availableProviders++; + if (info.activityInfo != null && info.activityInfo.enabled && info.activityInfo.exported) { + availableProviders++; + } } return availableProviders > 0; From 10dfcbf0b9063bc7572f44cedaac5e48784484d4 Mon Sep 17 00:00:00 2001 From: kapodamy Date: Sat, 17 Aug 2019 13:38:33 -0300 Subject: [PATCH 5/5] add manual switch in download setting fragment switch for: * Java I/O Api * Storage Access Framework --- .../newpipe/download/DownloadDialog.java | 4 +- .../settings/DownloadSettingsFragment.java | 6 +- .../newpipe/settings/NewPipeSettings.java | 56 +++---------------- .../util/FilePickerActivityHelper.java | 8 ++- .../giga/service/DownloadManager.java | 4 +- .../giga/ui/fragment/MissionsFragment.java | 48 ++++++++++++---- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/download_settings.xml | 7 +++ 10 files changed, 73 insertions(+), 69 deletions(-) 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 a9afc93d5..f77daa092 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -329,7 +329,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck return; } - if (data.getData().getAuthority() != null && data.getData().getAuthority().startsWith(context.getPackageName())) { + if (FilePickerActivityHelper.isOwnFileUri(context, data.getData())) { File file = Utils.getFileForUri(data.getData()); checkSelectedDownload(null, Uri.fromFile(file), file.getName(), StoredFileHelper.DEFAULT_MIME); return; @@ -592,7 +592,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck if (!askForSavePath) Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); - if (NewPipeSettings.hasCreateDocumentSupport) { + if (NewPipeSettings.useStorageAccessFramework(context)) { StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_SAVE_AS, filename, mime); } else { File initialSavePath; 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 db7754caf..7c2cb46e9 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java @@ -178,7 +178,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { } Intent i; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.hasOpenDocumentTreeSupport) { + 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); @@ -226,7 +226,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { forgetSAFTree(ctx, defaultPreferences.getString(key, "")); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.hasOpenDocumentTreeSupport) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !FilePickerActivityHelper.isOwnFileUri(ctx, uri)) { // steps to acquire the selected path: // 1. acquire permissions on the new save path // 2. save the new path, if step(2) was successful @@ -244,7 +244,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { return; } } else { - File target = Utils.getFileForUri(data.getData()); + File target = Utils.getFileForUri(uri); if (!target.canWrite()) { showMessageDialog(R.string.download_to_sdcard_error_title, R.string.download_to_sdcard_error_message); return; 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 6f26534fb..44da38c35 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -21,11 +21,7 @@ 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; import android.support.annotation.NonNull; @@ -33,7 +29,6 @@ import android.support.annotation.NonNull; import org.schabi.newpipe.R; import java.io.File; -import java.util.List; /** * Helper for global settings @@ -62,20 +57,6 @@ 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); @@ -85,17 +66,8 @@ public class NewPipeSettings { PreferenceManager.setDefaultValues(context, R.xml.video_audio_settings, true); PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - hasOpenDocumentTreeSupport = testFor(context, Intent.ACTION_OPEN_DOCUMENT_TREE, false); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - hasCreateDocumentSupport = testFor(context, Intent.ACTION_CREATE_DOCUMENT, true); - } - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !hasOpenDocumentTreeSupport) { - getVideoDownloadFolder(context); - getAudioDownloadFolder(context); - } + getVideoDownloadFolder(context); + getAudioDownloadFolder(context); } private static void getVideoDownloadFolder(Context context) { @@ -126,25 +98,11 @@ public class NewPipeSettings { return new File(dir, "NewPipe").toURI().toString(); } - private static boolean testFor(@NonNull Context ctx, @NonNull String intentAction, boolean isFile) { - Intent queryIntent = new Intent(intentAction) - .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + public static boolean useStorageAccessFramework(Context context) { + final String key = context.getString(R.string.storage_use_saf); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (isFile) { - queryIntent.setType("*/*"); - queryIntent.addCategory(Intent.CATEGORY_OPENABLE); - } - - List infoList = ctx.getPackageManager() - .queryIntentActivities(queryIntent, PackageManager.MATCH_DEFAULT_ONLY); - - int availableProviders = 0; - for (ResolveInfo info : infoList) { - if (info.activityInfo != null && info.activityInfo.enabled && info.activityInfo.exported) { - availableProviders++; - } - } - - return availableProviders > 0; + return prefs.getBoolean(key, false); } + } diff --git a/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java index 20554ce59..8e70e4f8d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; @@ -29,7 +30,7 @@ public class FilePickerActivityHelper extends com.nononsenseapps.filepicker.File @Override public void onCreate(Bundle savedInstanceState) { - if(ThemeHelper.isLightThemeSelected(this)) { + if (ThemeHelper.isLightThemeSelected(this)) { this.setTheme(R.style.FilePickerThemeLight); } else { this.setTheme(R.style.FilePickerThemeDark); @@ -73,6 +74,11 @@ public class FilePickerActivityHelper extends com.nononsenseapps.filepicker.File .putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_NEW_FILE); } + public static boolean isOwnFileUri(@NonNull Context context, @NonNull Uri uri) { + if (uri.getAuthority() == null) return false; + return uri.getAuthority().startsWith(context.getPackageName()); + } + /*////////////////////////////////////////////////////////////////////////// // Internal //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java index b168ef474..beb5e6dc9 100644 --- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java @@ -35,8 +35,8 @@ public class DownloadManager { public final static int SPECIAL_PENDING = 1; public final static int SPECIAL_FINISHED = 2; - static final String TAG_AUDIO = "audio"; - static final String TAG_VIDEO = "video"; + public static final String TAG_AUDIO = "audio"; + public static final String TAG_VIDEO = "video"; private final FinishedMissionStore mFinishedMissionStore; diff --git a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java index 69c8e8f50..2b2c09750 100644 --- a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java +++ b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java @@ -7,7 +7,9 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Bundle; +import android.os.Environment; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.annotation.NonNull; @@ -22,9 +24,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import com.nononsenseapps.filepicker.Utils; + import org.schabi.newpipe.R; +import org.schabi.newpipe.settings.NewPipeSettings; +import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.ThemeHelper; +import java.io.File; import java.io.IOException; import us.shandian.giga.get.DownloadMission; @@ -37,7 +44,7 @@ import us.shandian.giga.ui.adapter.MissionAdapter; public class MissionsFragment extends Fragment { private static final int SPAN_SIZE = 2; - private static final int REQUEST_DOWNLOAD_PATH_SAF = 0x1230; + private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230; private SharedPreferences mPrefs; private boolean mLinear; @@ -242,12 +249,28 @@ public class MissionsFragment extends Fragment { private void recoverMission(@NonNull DownloadMission mission) { unsafeMissionTarget = mission; - StoredFileHelper.requestSafWithFileCreation( - MissionsFragment.this, - REQUEST_DOWNLOAD_PATH_SAF, - mission.storage.getName(), - mission.storage.getType() - ); + + if (NewPipeSettings.useStorageAccessFramework(mContext)) { + StoredFileHelper.requestSafWithFileCreation( + MissionsFragment.this, + REQUEST_DOWNLOAD_SAVE_AS, + mission.storage.getName(), + mission.storage.getType() + ); + + } else { + File initialSavePath; + if (DownloadManager.TAG_VIDEO.equals(mission.storage.getType())) + initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MOVIES); + else + initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MUSIC); + + initialSavePath = new File(initialSavePath, mission.storage.getName()); + startActivityForResult( + FilePickerActivityHelper.chooseFileToSave(mContext, initialSavePath.getAbsolutePath()), + REQUEST_DOWNLOAD_SAVE_AS + ); + } } @Override @@ -290,15 +313,20 @@ public class MissionsFragment extends Fragment { public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode != REQUEST_DOWNLOAD_PATH_SAF || resultCode != Activity.RESULT_OK) return; + if (requestCode != REQUEST_DOWNLOAD_SAVE_AS || resultCode != Activity.RESULT_OK) return; if (unsafeMissionTarget == null || data.getData() == null) { - return;// unsafeMissionTarget cannot be null + return; } try { + Uri fileUri = data.getData(); + if (fileUri.getAuthority() != null && FilePickerActivityHelper.isOwnFileUri(mContext, fileUri)) { + fileUri = Uri.fromFile(Utils.getFileForUri(fileUri)); + } + String tag = unsafeMissionTarget.storage.getTag(); - unsafeMissionTarget.storage = new StoredFileHelper(mContext, null, data.getData(), tag); + unsafeMissionTarget.storage = new StoredFileHelper(mContext, null, fileUri, tag); mAdapter.recoverMission(unsafeMissionTarget); } catch (IOException e) { Toast.makeText(mContext, R.string.general_error, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fcc5afda9..028f146fc 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -458,7 +458,9 @@ abrir en modo popup Preguntar dónde descargar Se preguntará dónde guardar cada descarga - Se preguntará dónde guardar cada descarga.\nHabilita esta opción si quieres descargar en la tarjeta SD externa + Se preguntará dónde guardar cada descarga.\nHabilita esta opción junto con SAF si quieres descargar en la tarjeta SD externa + Usar SAF + El Framework de Acceso al Almacenamiento permite descargar en la tarjeta SD externa.\nNota: Algunos los dispositivos no son compatibles Desuscribirse Nueva pestaña diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 49f38b667..6c51fbb1c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -163,6 +163,7 @@ clear_search_history downloads_storage_ask + storage_use_saf file_rename_charset diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 91b0953cc..721fbcac4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -556,6 +556,8 @@ Ask where to download You will be asked where to save each download - You will be asked where to save each download.\nEnable this option if you want download to the external SD Card + You will be asked where to save each download.\nEnable this option with SAF if you want download to the external SD Card + Use SAF + The Storage Access Framework allow download to the external SD Card.\nNote: some devices are not compatible diff --git a/app/src/main/res/xml/download_settings.xml b/app/src/main/res/xml/download_settings.xml index 7a6fab841..0df021842 100644 --- a/app/src/main/res/xml/download_settings.xml +++ b/app/src/main/res/xml/download_settings.xml @@ -12,6 +12,13 @@ android:summary="@string/downloads_storage_ask_summary_kitkat" android:title="@string/downloads_storage_ask_title" /> + +