mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Merge pull request #2521 from kapodamy/saf-workarround
Downloads: add switch for saf/legacy file picker
This commit is contained in:
		| @@ -60,6 +60,7 @@ import org.schabi.newpipe.report.ErrorActivity; | |||||||
| import org.schabi.newpipe.util.Constants; | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.KioskTranslator; | import org.schabi.newpipe.util.KioskTranslator; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
|  | import org.schabi.newpipe.util.PermissionHelper; | ||||||
| import org.schabi.newpipe.util.ServiceHelper; | import org.schabi.newpipe.util.ServiceHelper; | ||||||
| import org.schabi.newpipe.util.StateSaver; | import org.schabi.newpipe.util.StateSaver; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
| @@ -421,6 +422,17 @@ public class MainActivity extends AppCompatActivity { | |||||||
|                 return; |                 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; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -74,10 +74,13 @@ import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr; | |||||||
|  */ |  */ | ||||||
| public class RouterActivity extends AppCompatActivity { | public class RouterActivity extends AppCompatActivity { | ||||||
|  |  | ||||||
|     @State protected int currentServiceId = -1; |     @State | ||||||
|  |     protected int currentServiceId = -1; | ||||||
|     private StreamingService currentService; |     private StreamingService currentService; | ||||||
|     @State protected LinkType currentLinkType; |     @State | ||||||
|     @State protected int selectedRadioPosition = -1; |     protected LinkType currentLinkType; | ||||||
|  |     @State | ||||||
|  |     protected int selectedRadioPosition = -1; | ||||||
|     protected int selectedPreviously = -1; |     protected int selectedPreviously = -1; | ||||||
|  |  | ||||||
|     protected String currentUrl; |     protected String currentUrl; | ||||||
| @@ -257,7 +260,7 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|                 .setNegativeButton(R.string.just_once, dialogButtonsClickListener) |                 .setNegativeButton(R.string.just_once, dialogButtonsClickListener) | ||||||
|                 .setPositiveButton(R.string.always, dialogButtonsClickListener) |                 .setPositiveButton(R.string.always, dialogButtonsClickListener) | ||||||
|                 .setOnDismissListener((dialog) -> { |                 .setOnDismissListener((dialog) -> { | ||||||
|                     if(!selectionIsDownload) finish(); |                     if (!selectionIsDownload) finish(); | ||||||
|                 }) |                 }) | ||||||
|                 .create(); |                 .create(); | ||||||
|  |  | ||||||
| @@ -358,13 +361,13 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|         positiveButton.setEnabled(state); |         positiveButton.setEnabled(state); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void handleText(){ |     private void handleText() { | ||||||
|         String searchString = getIntent().getStringExtra(Intent.EXTRA_TEXT); |         String searchString = getIntent().getStringExtra(Intent.EXTRA_TEXT); | ||||||
|         int serviceId = getIntent().getIntExtra(Constants.KEY_SERVICE_ID, 0); |         int serviceId = getIntent().getIntExtra(Constants.KEY_SERVICE_ID, 0); | ||||||
|         Intent intent = new Intent(getThemeWrapperContext(), MainActivity.class); |         Intent intent = new Intent(getThemeWrapperContext(), MainActivity.class); | ||||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|         startActivity(intent); |         startActivity(intent); | ||||||
|         NavigationHelper.openSearch(getThemeWrapperContext(),serviceId,searchString); |         NavigationHelper.openSearch(getThemeWrapperContext(), serviceId, searchString); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void handleChoice(final String selectedChoiceKey) { |     private void handleChoice(final String selectedChoiceKey) { | ||||||
| @@ -382,8 +385,10 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (selectedChoiceKey.equals(getString(R.string.download_key))) { |         if (selectedChoiceKey.equals(getString(R.string.download_key))) { | ||||||
|             selectionIsDownload = true; |             if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { | ||||||
|             openDownloadDialog(); |                 selectionIsDownload = true; | ||||||
|  |                 openDownloadDialog(); | ||||||
|  |             } | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -395,7 +400,7 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|                     .subscribeOn(Schedulers.io()) |                     .subscribeOn(Schedulers.io()) | ||||||
|                     .observeOn(AndroidSchedulers.mainThread()) |                     .observeOn(AndroidSchedulers.mainThread()) | ||||||
|                     .subscribe(intent -> { |                     .subscribe(intent -> { | ||||||
|                         if(!internalRoute){ |                         if (!internalRoute) { | ||||||
|                             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |                             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|                             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); |                             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); | ||||||
|                         } |                         } | ||||||
| @@ -445,17 +450,21 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||||
|         for (int i: grantResults){ |         for (int i : grantResults) { | ||||||
|             if (i == PackageManager.PERMISSION_DENIED){ |             if (i == PackageManager.PERMISSION_DENIED) { | ||||||
|                 finish(); |                 finish(); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) { | ||||||
|  |             openDownloadDialog(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static class AdapterChoiceItem { |     private static class AdapterChoiceItem { | ||||||
|         final String description, key; |         final String description, key; | ||||||
|         @DrawableRes final int icon; |         @DrawableRes | ||||||
|  |         final int icon; | ||||||
|  |  | ||||||
|         AdapterChoiceItem(String key, String description, int icon) { |         AdapterChoiceItem(String key, String description, int icon) { | ||||||
|             this.description = description; |             this.description = description; | ||||||
| @@ -553,7 +562,8 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|                 final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); |                 final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); | ||||||
|                 boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false); |                 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; |                 PlayQueue playQueue; | ||||||
|                 String playerChoice = choice.playerChoice; |                 String playerChoice = choice.playerChoice; | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import android.content.ServiceConnection; | |||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
|  | import android.os.Environment; | ||||||
| import android.os.IBinder; | import android.os.IBinder; | ||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.annotation.IdRes; | import android.support.annotation.IdRes; | ||||||
| @@ -33,6 +34,8 @@ import android.widget.Spinner; | |||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import com.nononsenseapps.filepicker.Utils; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.MainActivity; | import org.schabi.newpipe.MainActivity; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
| @@ -45,13 +48,17 @@ import org.schabi.newpipe.extractor.stream.VideoStream; | |||||||
| import org.schabi.newpipe.extractor.utils.Localization; | import org.schabi.newpipe.extractor.utils.Localization; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | 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.FilenameUtils; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
|  | import org.schabi.newpipe.util.PermissionHelper; | ||||||
| import org.schabi.newpipe.util.SecondaryStreamHelper; | import org.schabi.newpipe.util.SecondaryStreamHelper; | ||||||
| import org.schabi.newpipe.util.StreamItemAdapter; | import org.schabi.newpipe.util.StreamItemAdapter; | ||||||
| import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; | import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -72,7 +79,7 @@ import us.shandian.giga.service.MissionState; | |||||||
| public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { | public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { | ||||||
|     private static final String TAG = "DialogFragment"; |     private static final String TAG = "DialogFragment"; | ||||||
|     private static final boolean DEBUG = MainActivity.DEBUG; |     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 |     @State | ||||||
|     protected StreamInfo currentInfo; |     protected StreamInfo currentInfo; | ||||||
| @@ -173,6 +180,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|         if (DEBUG) |         if (DEBUG) | ||||||
|             Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); |             Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); | ||||||
|  |  | ||||||
|  |         if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { | ||||||
|  |             getDialog().dismiss(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         context = getContext(); |         context = getContext(); | ||||||
|  |  | ||||||
|         setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); |         setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); | ||||||
| @@ -311,12 +323,18 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { |     public void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||||
|         super.onActivityResult(requestCode, resultCode, 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) { |             if (data.getData() == null) { | ||||||
|                 showFailedDialog(R.string.general_error); |                 showFailedDialog(R.string.general_error); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             if (FilePickerActivityHelper.isOwnFileUri(context, data.getData())) { | ||||||
|  |                 File file = Utils.getFileForUri(data.getData()); | ||||||
|  |                 checkSelectedDownload(null, Uri.fromFile(file), file.getName(), StoredFileHelper.DEFAULT_MIME); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             DocumentFile docFile = DocumentFile.fromSingleUri(context, data.getData()); |             DocumentFile docFile = DocumentFile.fromSingleUri(context, data.getData()); | ||||||
|             if (docFile == null) { |             if (docFile == null) { | ||||||
|                 showFailedDialog(R.string.general_error); |                 showFailedDialog(R.string.general_error); | ||||||
| @@ -569,12 +587,27 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|             // This part is called if with SAF preferred: |             // This part is called if with SAF preferred: | ||||||
|             //  * older android version running |             //  * older android version running | ||||||
|             //  * save path not defined (via download settings) |             //  * 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) |             if (!askForSavePath) | ||||||
|                 Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); |                 Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show(); | ||||||
|  |  | ||||||
|             StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_PATH_SAF, filename, mime); |             if (NewPipeSettings.useStorageAccessFramework(context)) { | ||||||
|  |                 StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_SAVE_AS, filename, mime); | ||||||
|  |             } 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; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -624,6 +657,11 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|                     // This part is called if: |                     // This part is called if: | ||||||
|                     // * using SAF on older android version |                     // * using SAF on older android version | ||||||
|                     // * save path not defined |                     // * 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); |                     continueSelectedDownload(storage); | ||||||
|                     return; |                     return; | ||||||
|                 } else if (targetFile == null) { |                 } else if (targetFile == null) { | ||||||
| @@ -728,7 +766,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|         try { |         try { | ||||||
|             if (storage.length() > 0) storage.truncate(); |             if (storage.length() > 0) storage.truncate(); | ||||||
|         } catch (IOException e) { |         } 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); |             showFailedDialog(R.string.overwrite_failed); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -384,7 +384,10 @@ public class VideoDetailFragment | |||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             case R.id.detail_controls_download: |             case R.id.detail_controls_download: | ||||||
|                 this.openDownloadDialog(); |                 if (PermissionHelper.checkStoragePermissions(activity, | ||||||
|  |                         PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { | ||||||
|  |                     this.openDownloadDialog(); | ||||||
|  |                 } | ||||||
|                 break; |                 break; | ||||||
|             case R.id.detail_uploader_root_layout: |             case R.id.detail_uploader_root_layout: | ||||||
|                 if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) { |                 if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) { | ||||||
|   | |||||||
| @@ -35,8 +35,6 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|     private String DOWNLOAD_PATH_VIDEO_PREFERENCE; |     private String DOWNLOAD_PATH_VIDEO_PREFERENCE; | ||||||
|     private String DOWNLOAD_PATH_AUDIO_PREFERENCE; |     private String DOWNLOAD_PATH_AUDIO_PREFERENCE; | ||||||
|  |  | ||||||
|     private String DOWNLOAD_STORAGE_ASK; |  | ||||||
|  |  | ||||||
|     private Preference prefPathVideo; |     private Preference prefPathVideo; | ||||||
|     private Preference prefPathAudio; |     private Preference prefPathAudio; | ||||||
|     private Preference prefStorageAsk; |     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_VIDEO_PREFERENCE = getString(R.string.download_path_video_key); | ||||||
|         DOWNLOAD_PATH_AUDIO_PREFERENCE = getString(R.string.download_path_audio_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); |         prefPathVideo = findPreference(DOWNLOAD_PATH_VIDEO_PREFERENCE); | ||||||
|         prefPathAudio = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE); |         prefPathAudio = findPreference(DOWNLOAD_PATH_AUDIO_PREFERENCE); | ||||||
|         prefStorageAsk = findPreference(DOWNLOAD_STORAGE_ASK); |         prefStorageAsk = findPreference(downloadStorageAsk); | ||||||
|  |  | ||||||
|         updatePreferencesSummary(); |         updatePreferencesSummary(); | ||||||
|         updatePathPickers(!defaultPreferences.getBoolean(DOWNLOAD_STORAGE_ASK, false)); |         updatePathPickers(!defaultPreferences.getBoolean(downloadStorageAsk, false)); | ||||||
|  |  | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||||||
|             prefStorageAsk.setSummary(R.string.downloads_storage_ask_summary); |             prefStorageAsk.setSummary(R.string.downloads_storage_ask_summary); | ||||||
| @@ -180,7 +178,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         Intent i; |         Intent i; | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && NewPipeSettings.useStorageAccessFramework(ctx)) { | ||||||
|             i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) |             i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) | ||||||
|                     .putExtra("android.content.extra.SHOW_ADVANCED", true) |                     .putExtra("android.content.extra.SHOW_ADVANCED", true) | ||||||
|                     .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | StoredDirectoryHelper.PERMISSION_FLAGS); |                     .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | StoredDirectoryHelper.PERMISSION_FLAGS); | ||||||
| @@ -221,16 +219,17 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|             return; |             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 && !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 | ||||||
|             try { |             try { | ||||||
|                 ctx.grantUriPermission(ctx.getPackageName(), uri, StoredDirectoryHelper.PERMISSION_FLAGS); |                 ctx.grantUriPermission(ctx.getPackageName(), uri, StoredDirectoryHelper.PERMISSION_FLAGS); | ||||||
|  |  | ||||||
| @@ -245,7 +244,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             File target = Utils.getFileForUri(data.getData()); |             File target = Utils.getFileForUri(uri); | ||||||
|             if (!target.canWrite()) { |             if (!target.canWrite()) { | ||||||
|                 showMessageDialog(R.string.download_to_sdcard_error_title, R.string.download_to_sdcard_error_message); |                 showMessageDialog(R.string.download_to_sdcard_error_title, R.string.download_to_sdcard_error_message); | ||||||
|                 return; |                 return; | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ package org.schabi.newpipe.settings; | |||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| import android.os.Build; |  | ||||||
| import android.os.Environment; | import android.os.Environment; | ||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -67,10 +66,8 @@ public class NewPipeSettings { | |||||||
|         PreferenceManager.setDefaultValues(context, R.xml.video_audio_settings, true); |         PreferenceManager.setDefaultValues(context, R.xml.video_audio_settings, true); | ||||||
|         PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); |         PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); | ||||||
|  |  | ||||||
|         if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |         getVideoDownloadFolder(context); | ||||||
|             getVideoDownloadFolder(context); |         getAudioDownloadFolder(context); | ||||||
|             getAudioDownloadFolder(context); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static void getVideoDownloadFolder(Context context) { |     private static void getVideoDownloadFolder(Context context) { | ||||||
| @@ -93,11 +90,19 @@ public class NewPipeSettings { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @NonNull |     @NonNull | ||||||
|     private static File getDir(String defaultDirectoryName) { |     public static File getDir(String defaultDirectoryName) { | ||||||
|         return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName); |         return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static String getNewPipeChildFolderPathForDir(File dir) { |     private static String getNewPipeChildFolderPathForDir(File dir) { | ||||||
|         return new File(dir, "NewPipe").toURI().toString(); |         return new File(dir, "NewPipe").toURI().toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static boolean useStorageAccessFramework(Context context) { | ||||||
|  |         final String key = context.getString(R.string.storage_use_saf); | ||||||
|  |         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); | ||||||
|  |  | ||||||
|  |         return prefs.getBoolean(key, false); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package org.schabi.newpipe.util; | |||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
|  | import android.net.Uri; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.Environment; | import android.os.Environment; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -29,7 +30,7 @@ public class FilePickerActivityHelper extends com.nononsenseapps.filepicker.File | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCreate(Bundle savedInstanceState) { |     public void onCreate(Bundle savedInstanceState) { | ||||||
|         if(ThemeHelper.isLightThemeSelected(this)) { |         if (ThemeHelper.isLightThemeSelected(this)) { | ||||||
|             this.setTheme(R.style.FilePickerThemeLight); |             this.setTheme(R.style.FilePickerThemeLight); | ||||||
|         } else { |         } else { | ||||||
|             this.setTheme(R.style.FilePickerThemeDark); |             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); |                 .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 |     // Internal | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|   | |||||||
| @@ -446,6 +446,9 @@ public class NavigationHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static boolean openDownloads(Activity activity) { |     public static boolean openDownloads(Activity activity) { | ||||||
|  |         if (!PermissionHelper.checkStoragePermissions(activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|         Intent intent = new Intent(activity, DownloadActivity.class); |         Intent intent = new Intent(activity, DownloadActivity.class); | ||||||
|         activity.startActivity(intent); |         activity.startActivity(intent); | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -18,10 +18,12 @@ import android.widget.Toast; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  |  | ||||||
| public class PermissionHelper { | 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) { |     public static boolean checkStoragePermissions(Activity activity, int requestCode) { | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |         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); |         return checkWriteStoragePermissions(activity, requestCode); | ||||||
|     } |     } | ||||||
| @@ -89,7 +91,7 @@ public class PermissionHelper { | |||||||
|             i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |             i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|             context.startActivity(i); |             context.startActivity(i); | ||||||
|             return false; |             return false; | ||||||
|         }else return true; |         } else return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static boolean isPopupEnabled(Context context) { |     public static boolean isPopupEnabled(Context context) { | ||||||
|   | |||||||
| @@ -35,8 +35,8 @@ public class DownloadManager { | |||||||
|     public final static int SPECIAL_PENDING = 1; |     public final static int SPECIAL_PENDING = 1; | ||||||
|     public final static int SPECIAL_FINISHED = 2; |     public final static int SPECIAL_FINISHED = 2; | ||||||
|  |  | ||||||
|     static final String TAG_AUDIO = "audio"; |     public static final String TAG_AUDIO = "audio"; | ||||||
|     static final String TAG_VIDEO = "video"; |     public static final String TAG_VIDEO = "video"; | ||||||
|  |  | ||||||
|     private final FinishedMissionStore mFinishedMissionStore; |     private final FinishedMissionStore mFinishedMissionStore; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,9 @@ import android.content.Context; | |||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.ServiceConnection; | import android.content.ServiceConnection; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
|  | import android.net.Uri; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
|  | import android.os.Environment; | ||||||
| import android.os.IBinder; | import android.os.IBinder; | ||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| @@ -22,9 +24,14 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import com.nononsenseapps.filepicker.Utils; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.settings.NewPipeSettings; | ||||||
|  | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
| import org.schabi.newpipe.util.ThemeHelper; | import org.schabi.newpipe.util.ThemeHelper; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| import us.shandian.giga.get.DownloadMission; | import us.shandian.giga.get.DownloadMission; | ||||||
| @@ -37,7 +44,7 @@ import us.shandian.giga.ui.adapter.MissionAdapter; | |||||||
| public class MissionsFragment extends Fragment { | public class MissionsFragment extends Fragment { | ||||||
|  |  | ||||||
|     private static final int SPAN_SIZE = 2; |     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 SharedPreferences mPrefs; | ||||||
|     private boolean mLinear; |     private boolean mLinear; | ||||||
| @@ -242,12 +249,28 @@ public class MissionsFragment extends Fragment { | |||||||
|  |  | ||||||
|     private void recoverMission(@NonNull DownloadMission mission) { |     private void recoverMission(@NonNull DownloadMission mission) { | ||||||
|         unsafeMissionTarget = mission; |         unsafeMissionTarget = mission; | ||||||
|         StoredFileHelper.requestSafWithFileCreation( |  | ||||||
|                 MissionsFragment.this, |         if (NewPipeSettings.useStorageAccessFramework(mContext)) { | ||||||
|                 REQUEST_DOWNLOAD_PATH_SAF, |             StoredFileHelper.requestSafWithFileCreation( | ||||||
|                 mission.storage.getName(), |                     MissionsFragment.this, | ||||||
|                 mission.storage.getType() |                     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 |     @Override | ||||||
| @@ -290,15 +313,20 @@ public class MissionsFragment extends Fragment { | |||||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { |     public void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||||
|         super.onActivityResult(requestCode, resultCode, 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) { |         if (unsafeMissionTarget == null || data.getData() == null) { | ||||||
|             return;// unsafeMissionTarget cannot be null |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|  |             Uri fileUri = data.getData(); | ||||||
|  |             if (fileUri.getAuthority() != null && FilePickerActivityHelper.isOwnFileUri(mContext, fileUri)) { | ||||||
|  |                 fileUri = Uri.fromFile(Utils.getFileForUri(fileUri)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             String tag = unsafeMissionTarget.storage.getTag(); |             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); |             mAdapter.recoverMission(unsafeMissionTarget); | ||||||
|         } catch (IOException e) { |         } catch (IOException e) { | ||||||
|             Toast.makeText(mContext, R.string.general_error, Toast.LENGTH_LONG).show(); |             Toast.makeText(mContext, R.string.general_error, Toast.LENGTH_LONG).show(); | ||||||
|   | |||||||
| @@ -458,7 +458,9 @@ abrir en modo popup</string> | |||||||
|  |  | ||||||
|     <string name="downloads_storage_ask_title">Preguntar dónde descargar</string> |     <string name="downloads_storage_ask_title">Preguntar dónde descargar</string> | ||||||
|     <string name="downloads_storage_ask_summary">Se preguntará dónde guardar cada descarga</string> |     <string name="downloads_storage_ask_summary">Se preguntará dónde guardar cada descarga</string> | ||||||
|     <string name="downloads_storage_ask_summary_kitkat">Se preguntará dónde guardar cada descarga.\nHabilita esta opción si quieres descargar en la tarjeta SD externa</string> |     <string name="downloads_storage_ask_summary_kitkat">Se preguntará dónde guardar cada descarga.\nHabilita esta opción junto con SAF si quieres descargar en la tarjeta SD externa</string> | ||||||
|  |     <string name="downloads_storage_use_saf_title">Usar SAF</string> | ||||||
|  |     <string name="downloads_storage_use_saf_summary">El Framework de Acceso al Almacenamiento permite descargar en la tarjeta SD externa.\nNota: Algunos los dispositivos no son compatibles</string> | ||||||
|  |  | ||||||
|     <string name="unsubscribe">Desuscribirse</string> |     <string name="unsubscribe">Desuscribirse</string> | ||||||
|     <string name="tab_new">Nueva pestaña</string> |     <string name="tab_new">Nueva pestaña</string> | ||||||
|   | |||||||
| @@ -163,6 +163,7 @@ | |||||||
|     <string name="clear_search_history_key" translatable="false">clear_search_history</string> |     <string name="clear_search_history_key" translatable="false">clear_search_history</string> | ||||||
|  |  | ||||||
|     <string name="downloads_storage_ask" translatable="false">downloads_storage_ask</string> |     <string name="downloads_storage_ask" translatable="false">downloads_storage_ask</string> | ||||||
|  |     <string name="storage_use_saf" translatable="false">storage_use_saf</string> | ||||||
|  |  | ||||||
|     <!-- FileName Downloads  --> |     <!-- FileName Downloads  --> | ||||||
|     <string name="settings_file_charset_key" translatable="false">file_rename_charset</string> |     <string name="settings_file_charset_key" translatable="false">file_rename_charset</string> | ||||||
|   | |||||||
| @@ -556,6 +556,8 @@ | |||||||
|  |  | ||||||
|     <string name="downloads_storage_ask_title">Ask where to download</string> |     <string name="downloads_storage_ask_title">Ask where to download</string> | ||||||
|     <string name="downloads_storage_ask_summary">You will be asked where to save each download</string> |     <string name="downloads_storage_ask_summary">You will be asked where to save each download</string> | ||||||
|     <string name="downloads_storage_ask_summary_kitkat">You will be asked where to save each download.\nEnable this option if you want download to the external SD Card</string> |     <string name="downloads_storage_ask_summary_kitkat">You will be asked where to save each download.\nEnable this option with SAF if you want download to the external SD Card</string> | ||||||
|  |     <string name="downloads_storage_use_saf_title">Use SAF</string> | ||||||
|  |     <string name="downloads_storage_use_saf_summary">The Storage Access Framework allow download to the external SD Card.\nNote: some devices are not compatible</string> | ||||||
|  |  | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -12,6 +12,13 @@ | |||||||
|         android:summary="@string/downloads_storage_ask_summary_kitkat" |         android:summary="@string/downloads_storage_ask_summary_kitkat" | ||||||
|         android:title="@string/downloads_storage_ask_title" /> |         android:title="@string/downloads_storage_ask_title" /> | ||||||
|  |  | ||||||
|  |     <SwitchPreference | ||||||
|  |         app:iconSpaceReserved="false" | ||||||
|  |         android:defaultValue="false" | ||||||
|  |         android:key="@string/storage_use_saf" | ||||||
|  |         android:summary="@string/downloads_storage_use_saf_summary" | ||||||
|  |         android:title="@string/downloads_storage_use_saf_title" /> | ||||||
|  |  | ||||||
|     <Preference |     <Preference | ||||||
|         app:iconSpaceReserved="false" |         app:iconSpaceReserved="false" | ||||||
|         android:dialogTitle="@string/download_path_dialog_title" |         android:dialogTitle="@string/download_path_dialog_title" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Christian Schabesberger
					Christian Schabesberger