mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Merge pull request #6716 from TeamNewPipe/release_0.21.7
Hotfix release 0.21.7
This commit is contained in:
		| @@ -17,8 +17,8 @@ android { | |||||||
|         resValue "string", "app_name", "NewPipe" |         resValue "string", "app_name", "NewPipe" | ||||||
|         minSdkVersion 19 |         minSdkVersion 19 | ||||||
|         targetSdkVersion 29 |         targetSdkVersion 29 | ||||||
|         versionCode 972 |         versionCode 973 | ||||||
|         versionName "0.21.6" |         versionName "0.21.7" | ||||||
|  |  | ||||||
|         multiDexEnabled true |         multiDexEnabled true | ||||||
|  |  | ||||||
| @@ -201,7 +201,7 @@ dependencies { | |||||||
|     implementation 'androidx.constraintlayout:constraintlayout:2.0.4' |     implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | ||||||
|     implementation 'androidx.core:core-ktx:1.3.2' |     implementation 'androidx.core:core-ktx:1.3.2' | ||||||
|     implementation 'androidx.documentfile:documentfile:1.0.1' |     implementation 'androidx.documentfile:documentfile:1.0.1' | ||||||
|     implementation 'androidx.fragment:fragment-ktx:1.3.4' |     implementation 'androidx.fragment:fragment-ktx:1.3.5' | ||||||
|     implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}" |     implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}" | ||||||
|     implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}" |     implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}" | ||||||
|     implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' |     implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' | ||||||
|   | |||||||
| @@ -589,9 +589,9 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|                     downloadDialog.setVideoStreams(sortedVideoStreams); |                     downloadDialog.setVideoStreams(sortedVideoStreams); | ||||||
|                     downloadDialog.setAudioStreams(result.getAudioStreams()); |                     downloadDialog.setAudioStreams(result.getAudioStreams()); | ||||||
|                     downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); |                     downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); | ||||||
|  |                     downloadDialog.setOnDismissListener(dialog -> finish()); | ||||||
|                     downloadDialog.show(fm, "downloadDialog"); |                     downloadDialog.show(fm, "downloadDialog"); | ||||||
|                     fm.executePendingTransactions(); |                     fm.executePendingTransactions(); | ||||||
|                     downloadDialog.requireDialog().setOnDismissListener(dialog -> finish()); |  | ||||||
|                 }, throwable -> |                 }, throwable -> | ||||||
|                         showUnsupportedUrlDialog(currentUrl))); |                         showUnsupportedUrlDialog(currentUrl))); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ package org.schabi.newpipe.download; | |||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.ComponentName; | import android.content.ComponentName; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  | import android.content.DialogInterface; | ||||||
|  | import android.content.DialogInterface.OnDismissListener; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.ServiceConnection; | import android.content.ServiceConnection; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| @@ -20,6 +22,9 @@ import android.widget.RadioGroup; | |||||||
| import android.widget.SeekBar; | import android.widget.SeekBar; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import androidx.activity.result.ActivityResult; | ||||||
|  | import androidx.activity.result.ActivityResultLauncher; | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; | ||||||
| import androidx.annotation.IdRes; | import androidx.annotation.IdRes; | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| @@ -35,7 +40,6 @@ 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.RouterActivity; |  | ||||||
| import org.schabi.newpipe.databinding.DownloadDialogBinding; | import org.schabi.newpipe.databinding.DownloadDialogBinding; | ||||||
| import org.schabi.newpipe.error.ErrorActivity; | import org.schabi.newpipe.error.ErrorActivity; | ||||||
| import org.schabi.newpipe.error.ErrorInfo; | import org.schabi.newpipe.error.ErrorInfo; | ||||||
| @@ -82,9 +86,6 @@ public class DownloadDialog extends DialogFragment | |||||||
|         implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { |         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_SAVE_AS = 0x1230; |  | ||||||
|     private static final int REQUEST_DOWNLOAD_PICK_VIDEO_FOLDER = 0x789E; |  | ||||||
|     private static final int REQUEST_DOWNLOAD_PICK_AUDIO_FOLDER = 0x789F; |  | ||||||
|  |  | ||||||
|     @State |     @State | ||||||
|     StreamInfo currentInfo; |     StreamInfo currentInfo; | ||||||
| @@ -101,6 +102,9 @@ public class DownloadDialog extends DialogFragment | |||||||
|     @State |     @State | ||||||
|     int selectedSubtitleIndex = 0; |     int selectedSubtitleIndex = 0; | ||||||
|  |  | ||||||
|  |     @Nullable | ||||||
|  |     private OnDismissListener onDismissListener = null; | ||||||
|  |  | ||||||
|     private StoredDirectoryHelper mainStorageAudio = null; |     private StoredDirectoryHelper mainStorageAudio = null; | ||||||
|     private StoredDirectoryHelper mainStorageVideo = null; |     private StoredDirectoryHelper mainStorageVideo = null; | ||||||
|     private DownloadManager downloadManager = null; |     private DownloadManager downloadManager = null; | ||||||
| @@ -122,6 +126,21 @@ public class DownloadDialog extends DialogFragment | |||||||
|     private String filenameTmp; |     private String filenameTmp; | ||||||
|     private String mimeTmp; |     private String mimeTmp; | ||||||
|  |  | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadSaveAsLauncher = | ||||||
|  |             registerForActivityResult( | ||||||
|  |                     new StartActivityForResult(), this::requestDownloadSaveAsResult); | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadPickAudioFolderLauncher = | ||||||
|  |             registerForActivityResult( | ||||||
|  |                     new StartActivityForResult(), this::requestDownloadPickAudioFolderResult); | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadPickVideoFolderLauncher = | ||||||
|  |             registerForActivityResult( | ||||||
|  |                     new StartActivityForResult(), this::requestDownloadPickVideoFolderResult); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Instance creation | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     public static DownloadDialog newInstance(final StreamInfo info) { |     public static DownloadDialog newInstance(final StreamInfo info) { | ||||||
|         final DownloadDialog dialog = new DownloadDialog(); |         final DownloadDialog dialog = new DownloadDialog(); | ||||||
|         dialog.setInfo(info); |         dialog.setInfo(info); | ||||||
| @@ -143,6 +162,11 @@ public class DownloadDialog extends DialogFragment | |||||||
|         return instance; |         return instance; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Setters | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     private void setInfo(final StreamInfo info) { |     private void setInfo(final StreamInfo info) { | ||||||
|         this.currentInfo = info; |         this.currentInfo = info; | ||||||
|     } |     } | ||||||
| @@ -184,6 +208,14 @@ public class DownloadDialog extends DialogFragment | |||||||
|         this.selectedSubtitleIndex = ssi; |         this.selectedSubtitleIndex = ssi; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void setOnDismissListener(@Nullable final OnDismissListener onDismissListener) { | ||||||
|  |         this.onDismissListener = onDismissListener; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Android lifecycle | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCreate(@Nullable final Bundle savedInstanceState) { |     public void onCreate(@Nullable final Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
| @@ -194,7 +226,7 @@ public class DownloadDialog extends DialogFragment | |||||||
|  |  | ||||||
|         if (!PermissionHelper.checkStoragePermissions(getActivity(), |         if (!PermissionHelper.checkStoragePermissions(getActivity(), | ||||||
|                 PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { |                 PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { | ||||||
|             getDialog().dismiss(); |             dismiss(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -253,10 +285,6 @@ public class DownloadDialog extends DialogFragment | |||||||
|         }, Context.BIND_AUTO_CREATE); |         }, Context.BIND_AUTO_CREATE); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Inits |  | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, |     public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, | ||||||
|                              final Bundle savedInstanceState) { |                              final Bundle savedInstanceState) { | ||||||
| @@ -312,6 +340,60 @@ public class DownloadDialog extends DialogFragment | |||||||
|         fetchStreamsSize(); |         fetchStreamsSize(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private void initToolbar(final Toolbar toolbar) { | ||||||
|  |         if (DEBUG) { | ||||||
|  |             Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         toolbar.setTitle(R.string.download_dialog_title); | ||||||
|  |         toolbar.setNavigationIcon(R.drawable.ic_arrow_back); | ||||||
|  |         toolbar.inflateMenu(R.menu.dialog_url); | ||||||
|  |         toolbar.setNavigationOnClickListener(v -> dismiss()); | ||||||
|  |         toolbar.setNavigationContentDescription(R.string.cancel); | ||||||
|  |  | ||||||
|  |         okButton = toolbar.findViewById(R.id.okay); | ||||||
|  |         okButton.setEnabled(false); // disable until the download service connection is done | ||||||
|  |  | ||||||
|  |         toolbar.setOnMenuItemClickListener(item -> { | ||||||
|  |             if (item.getItemId() == R.id.okay) { | ||||||
|  |                 prepareSelectedDownload(); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDismiss(@NonNull final DialogInterface dialog) { | ||||||
|  |         super.onDismiss(dialog); | ||||||
|  |         if (onDismissListener != null) { | ||||||
|  |             onDismissListener.onDismiss(dialog); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDestroy() { | ||||||
|  |         super.onDestroy(); | ||||||
|  |         disposables.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onDestroyView() { | ||||||
|  |         dialogBinding = null; | ||||||
|  |         super.onDestroyView(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onSaveInstanceState(@NonNull final Bundle outState) { | ||||||
|  |         super.onSaveInstanceState(outState); | ||||||
|  |         Icepick.saveInstanceState(this, outState); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Video, audio and subtitle spinners | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     private void fetchStreamsSize() { |     private void fetchStreamsSize() { | ||||||
|         disposables.clear(); |         disposables.clear(); | ||||||
|         disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams) |         disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams) | ||||||
| @@ -346,126 +428,6 @@ public class DownloadDialog extends DialogFragment | |||||||
|                                 currentInfo.getServiceId())))); |                                 currentInfo.getServiceId())))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onDestroy() { |  | ||||||
|         super.onDestroy(); |  | ||||||
|         disposables.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onDestroyView() { |  | ||||||
|         dialogBinding = null; |  | ||||||
|         super.onDestroyView(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Radio group Video&Audio options - Listener |  | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onSaveInstanceState(@NonNull final Bundle outState) { |  | ||||||
|         super.onSaveInstanceState(outState); |  | ||||||
|         Icepick.saveInstanceState(this, outState); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Streams Spinner Listener |  | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { |  | ||||||
|         super.onActivityResult(requestCode, resultCode, data); |  | ||||||
|  |  | ||||||
|         if (resultCode != Activity.RESULT_OK) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (data.getData() == null) { |  | ||||||
|             showFailedDialog(R.string.general_error); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (requestCode == REQUEST_DOWNLOAD_SAVE_AS) { |  | ||||||
|             if (FilePickerActivityHelper.isOwnFileUri(context, data.getData())) { |  | ||||||
|                 final File file = Utils.getFileForUri(data.getData()); |  | ||||||
|                 checkSelectedDownload(null, Uri.fromFile(file), file.getName(), |  | ||||||
|                         StoredFileHelper.DEFAULT_MIME); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             final DocumentFile docFile = DocumentFile.fromSingleUri(context, data.getData()); |  | ||||||
|             if (docFile == null) { |  | ||||||
|                 showFailedDialog(R.string.general_error); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // check if the selected file was previously used |  | ||||||
|             checkSelectedDownload(null, data.getData(), docFile.getName(), |  | ||||||
|                     docFile.getType()); |  | ||||||
|         } else if (requestCode == REQUEST_DOWNLOAD_PICK_AUDIO_FOLDER |  | ||||||
|                 || requestCode == REQUEST_DOWNLOAD_PICK_VIDEO_FOLDER) { |  | ||||||
|             Uri uri = data.getData(); |  | ||||||
|             if (FilePickerActivityHelper.isOwnFileUri(context, uri)) { |  | ||||||
|                 uri = Uri.fromFile(Utils.getFileForUri(uri)); |  | ||||||
|             } else { |  | ||||||
|                 context.grantUriPermission(context.getPackageName(), uri, |  | ||||||
|                         StoredDirectoryHelper.PERMISSION_FLAGS); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             final String key; |  | ||||||
|             final String tag; |  | ||||||
|             if (requestCode == REQUEST_DOWNLOAD_PICK_AUDIO_FOLDER) { |  | ||||||
|                 key = getString(R.string.download_path_audio_key); |  | ||||||
|                 tag = DownloadManager.TAG_AUDIO; |  | ||||||
|             } else { |  | ||||||
|                 key = getString(R.string.download_path_video_key); |  | ||||||
|                 tag = DownloadManager.TAG_VIDEO; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             PreferenceManager.getDefaultSharedPreferences(context).edit() |  | ||||||
|                     .putString(key, uri.toString()).apply(); |  | ||||||
|  |  | ||||||
|             try { |  | ||||||
|                 final StoredDirectoryHelper mainStorage |  | ||||||
|                         = new StoredDirectoryHelper(context, uri, tag); |  | ||||||
|                 checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), |  | ||||||
|                         filenameTmp, mimeTmp); |  | ||||||
|             } catch (final IOException e) { |  | ||||||
|                 showFailedDialog(R.string.general_error); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void initToolbar(final Toolbar toolbar) { |  | ||||||
|         if (DEBUG) { |  | ||||||
|             Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         toolbar.setTitle(R.string.download_dialog_title); |  | ||||||
|         toolbar.setNavigationIcon(R.drawable.ic_arrow_back); |  | ||||||
|         toolbar.inflateMenu(R.menu.dialog_url); |  | ||||||
|         toolbar.setNavigationOnClickListener(v -> requireDialog().dismiss()); |  | ||||||
|         toolbar.setNavigationContentDescription(R.string.cancel); |  | ||||||
|  |  | ||||||
|         okButton = toolbar.findViewById(R.id.okay); |  | ||||||
|         okButton.setEnabled(false); // disable until the download service connection is done |  | ||||||
|  |  | ||||||
|         toolbar.setOnMenuItemClickListener(item -> { |  | ||||||
|             if (item.getItemId() == R.id.okay) { |  | ||||||
|                 prepareSelectedDownload(); |  | ||||||
|                 if (getActivity() instanceof RouterActivity) { |  | ||||||
|                     getActivity().finish(); |  | ||||||
|                 } |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Utils |  | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |  | ||||||
|  |  | ||||||
|     private void setupAudioSpinner() { |     private void setupAudioSpinner() { | ||||||
|         if (getContext() == null) { |         if (getContext() == null) { | ||||||
|             return; |             return; | ||||||
| @@ -496,6 +458,88 @@ public class DownloadDialog extends DialogFragment | |||||||
|         setRadioButtonsState(true); |         setRadioButtonsState(true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Activity results | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|  |     private void requestDownloadPickAudioFolderResult(final ActivityResult result) { | ||||||
|  |         requestDownloadPickFolderResult( | ||||||
|  |                 result, getString(R.string.download_path_audio_key), DownloadManager.TAG_AUDIO); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadPickVideoFolderResult(final ActivityResult result) { | ||||||
|  |         requestDownloadPickFolderResult( | ||||||
|  |                 result, getString(R.string.download_path_video_key), DownloadManager.TAG_VIDEO); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadSaveAsResult(final ActivityResult result) { | ||||||
|  |         if (result.getResultCode() != Activity.RESULT_OK) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (result.getData() == null || result.getData().getData() == null) { | ||||||
|  |             showFailedDialog(R.string.general_error); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (FilePickerActivityHelper.isOwnFileUri(context, result.getData().getData())) { | ||||||
|  |             final File file = Utils.getFileForUri(result.getData().getData()); | ||||||
|  |             checkSelectedDownload(null, Uri.fromFile(file), file.getName(), | ||||||
|  |                     StoredFileHelper.DEFAULT_MIME); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         final DocumentFile docFile | ||||||
|  |                 = DocumentFile.fromSingleUri(context, result.getData().getData()); | ||||||
|  |         if (docFile == null) { | ||||||
|  |             showFailedDialog(R.string.general_error); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // check if the selected file was previously used | ||||||
|  |         checkSelectedDownload(null, result.getData().getData(), docFile.getName(), | ||||||
|  |                 docFile.getType()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadPickFolderResult(final ActivityResult result, | ||||||
|  |                                                  final String key, | ||||||
|  |                                                  final String tag) { | ||||||
|  |         if (result.getResultCode() != Activity.RESULT_OK) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (result.getData() == null || result.getData().getData() == null) { | ||||||
|  |             showFailedDialog(R.string.general_error); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Uri uri = result.getData().getData(); | ||||||
|  |         if (FilePickerActivityHelper.isOwnFileUri(context, uri)) { | ||||||
|  |             uri = Uri.fromFile(Utils.getFileForUri(uri)); | ||||||
|  |         } else { | ||||||
|  |             context.grantUriPermission(context.getPackageName(), uri, | ||||||
|  |                     StoredDirectoryHelper.PERMISSION_FLAGS); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         PreferenceManager.getDefaultSharedPreferences(context).edit() | ||||||
|  |                 .putString(key, uri.toString()).apply(); | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             final StoredDirectoryHelper mainStorage | ||||||
|  |                     = new StoredDirectoryHelper(context, uri, tag); | ||||||
|  |             checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), | ||||||
|  |                     filenameTmp, mimeTmp); | ||||||
|  |         } catch (final IOException e) { | ||||||
|  |             showFailedDialog(R.string.general_error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Listeners | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCheckedChanged(final RadioGroup group, @IdRes final int checkedId) { |     public void onCheckedChanged(final RadioGroup group, @IdRes final int checkedId) { | ||||||
|         if (DEBUG) { |         if (DEBUG) { | ||||||
| @@ -545,6 +589,11 @@ public class DownloadDialog extends DialogFragment | |||||||
|     public void onNothingSelected(final AdapterView<?> parent) { |     public void onNothingSelected(final AdapterView<?> parent) { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Download | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     protected void setupDownloadOptions() { |     protected void setupDownloadOptions() { | ||||||
|         setRadioButtonsState(false); |         setRadioButtonsState(false); | ||||||
|  |  | ||||||
| @@ -557,7 +606,7 @@ public class DownloadDialog extends DialogFragment | |||||||
|         dialogBinding.subtitleButton.setVisibility(isSubtitleStreamsAvailable |         dialogBinding.subtitleButton.setVisibility(isSubtitleStreamsAvailable | ||||||
|                 ? View.VISIBLE : View.GONE); |                 ? View.VISIBLE : View.GONE); | ||||||
|  |  | ||||||
|         prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); |         prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); | ||||||
|         final String defaultMedia = prefs.getString(getString(R.string.last_used_download_type), |         final String defaultMedia = prefs.getString(getString(R.string.last_used_download_type), | ||||||
|                     getString(R.string.last_download_type_video_key)); |                     getString(R.string.last_download_type_video_key)); | ||||||
|  |  | ||||||
| @@ -585,7 +634,7 @@ public class DownloadDialog extends DialogFragment | |||||||
|         } else { |         } else { | ||||||
|             Toast.makeText(getContext(), R.string.no_streams_available_download, |             Toast.makeText(getContext(), R.string.no_streams_available_download, | ||||||
|                     Toast.LENGTH_SHORT).show(); |                     Toast.LENGTH_SHORT).show(); | ||||||
|             getDialog().dismiss(); |             dismiss(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -637,6 +686,10 @@ public class DownloadDialog extends DialogFragment | |||||||
|                 .show(); |                 .show(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private void launchDirectoryPicker(final ActivityResultLauncher<Intent> launcher) { | ||||||
|  |         launcher.launch(StoredDirectoryHelper.getPicker(context)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private void prepareSelectedDownload() { |     private void prepareSelectedDownload() { | ||||||
|         final StoredDirectoryHelper mainStorage; |         final StoredDirectoryHelper mainStorage; | ||||||
|         final MediaFormat format; |         final MediaFormat format; | ||||||
| @@ -691,11 +744,9 @@ public class DownloadDialog extends DialogFragment | |||||||
|                     Toast.LENGTH_LONG).show(); |                     Toast.LENGTH_LONG).show(); | ||||||
|  |  | ||||||
|             if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) { |             if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) { | ||||||
|                 startActivityForResult(StoredDirectoryHelper.getPicker(context), |                 launchDirectoryPicker(requestDownloadPickAudioFolderLauncher); | ||||||
|                         REQUEST_DOWNLOAD_PICK_AUDIO_FOLDER); |  | ||||||
|             } else { |             } else { | ||||||
|                 startActivityForResult(StoredDirectoryHelper.getPicker(context), |                 launchDirectoryPicker(requestDownloadPickVideoFolderLauncher); | ||||||
|                         REQUEST_DOWNLOAD_PICK_VIDEO_FOLDER); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return; |             return; | ||||||
| @@ -715,8 +766,8 @@ public class DownloadDialog extends DialogFragment | |||||||
|                 initialPath = Uri.parse(initialSavePath.getAbsolutePath()); |                 initialPath = Uri.parse(initialSavePath.getAbsolutePath()); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             startActivityForResult(StoredFileHelper.getNewPicker(context, |             requestDownloadSaveAsLauncher.launch(StoredFileHelper.getNewPicker(context, | ||||||
|                     filenameTmp, mimeTmp, initialPath), REQUEST_DOWNLOAD_SAVE_AS); |                     filenameTmp, mimeTmp, initialPath)); | ||||||
|  |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package org.schabi.newpipe.local; | package org.schabi.newpipe.local; | ||||||
|  |  | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| import android.content.res.Configuration; |  | ||||||
| import android.content.res.Resources; | import android.content.res.Resources; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
| @@ -26,6 +25,7 @@ import org.schabi.newpipe.fragments.list.ListViewContract; | |||||||
|  |  | ||||||
| import static org.schabi.newpipe.ktx.ViewUtils.animate; | import static org.schabi.newpipe.ktx.ViewUtils.animate; | ||||||
| import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; | import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; | ||||||
|  | import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This fragment is design to be used with persistent data such as |  * This fragment is design to be used with persistent data such as | ||||||
| @@ -77,7 +77,7 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I> | |||||||
|         super.onResume(); |         super.onResume(); | ||||||
|         if (updateFlags != 0) { |         if (updateFlags != 0) { | ||||||
|             if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) { |             if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) { | ||||||
|                 final boolean useGrid = isGridLayout(); |                 final boolean useGrid = shouldUseGridLayout(requireContext()); | ||||||
|                 itemsList.setLayoutManager( |                 itemsList.setLayoutManager( | ||||||
|                         useGrid ? getGridLayoutManager() : getListLayoutManager()); |                         useGrid ? getGridLayoutManager() : getListLayoutManager()); | ||||||
|                 itemListAdapter.setUseGridVariant(useGrid); |                 itemListAdapter.setUseGridVariant(useGrid); | ||||||
| @@ -121,7 +121,7 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I> | |||||||
|  |  | ||||||
|         itemListAdapter = new LocalItemListAdapter(activity); |         itemListAdapter = new LocalItemListAdapter(activity); | ||||||
|  |  | ||||||
|         final boolean useGrid = isGridLayout(); |         final boolean useGrid = shouldUseGridLayout(requireContext()); | ||||||
|         itemsList = rootView.findViewById(R.id.items_list); |         itemsList = rootView.findViewById(R.id.items_list); | ||||||
|         itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); |         itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); | ||||||
|  |  | ||||||
| @@ -260,17 +260,4 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I> | |||||||
|             updateFlags |= LIST_MODE_UPDATE_FLAG; |             updateFlags |= LIST_MODE_UPDATE_FLAG; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected boolean isGridLayout() { |  | ||||||
|         final String listMode = PreferenceManager.getDefaultSharedPreferences(activity) |  | ||||||
|                 .getString(getString(R.string.list_view_mode_key), |  | ||||||
|                         getString(R.string.list_view_mode_value)); |  | ||||||
|         if ("auto".equals(listMode)) { |  | ||||||
|             final Configuration configuration = getResources().getConfiguration(); |  | ||||||
|             return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE |  | ||||||
|                     && configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); |  | ||||||
|         } else { |  | ||||||
|             return "grid".equals(listMode); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ import android.annotation.SuppressLint | |||||||
| import android.app.Activity | import android.app.Activity | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.content.SharedPreferences | import android.content.SharedPreferences | ||||||
| import android.content.res.Configuration |  | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import android.os.Parcelable | import android.os.Parcelable | ||||||
| import android.view.LayoutInflater | import android.view.LayoutInflater | ||||||
| @@ -74,10 +73,10 @@ import org.schabi.newpipe.player.helper.PlayerHolder | |||||||
| import org.schabi.newpipe.util.Localization | import org.schabi.newpipe.util.Localization | ||||||
| import org.schabi.newpipe.util.NavigationHelper | import org.schabi.newpipe.util.NavigationHelper | ||||||
| import org.schabi.newpipe.util.StreamDialogEntry | import org.schabi.newpipe.util.StreamDialogEntry | ||||||
|  | import org.schabi.newpipe.util.ThemeHelper.getGridSpanCount | ||||||
|  | import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout | ||||||
| import java.time.OffsetDateTime | import java.time.OffsetDateTime | ||||||
| import java.util.ArrayList | import java.util.ArrayList | ||||||
| import kotlin.math.floor |  | ||||||
| import kotlin.math.max |  | ||||||
|  |  | ||||||
| class FeedFragment : BaseStateFragment<FeedState>() { | class FeedFragment : BaseStateFragment<FeedState>() { | ||||||
|     private var _feedBinding: FragmentFeedBinding? = null |     private var _feedBinding: FragmentFeedBinding? = null | ||||||
| @@ -161,7 +160,7 @@ class FeedFragment : BaseStateFragment<FeedState>() { | |||||||
|  |  | ||||||
|     fun setupListViewMode() { |     fun setupListViewMode() { | ||||||
|         // does everything needed to setup the layouts for grid or list modes |         // does everything needed to setup the layouts for grid or list modes | ||||||
|         groupAdapter.spanCount = if (shouldUseGridLayout()) getGridSpanCount() else 1 |         groupAdapter.spanCount = if (shouldUseGridLayout(context)) getGridSpanCount(context) else 1 | ||||||
|         feedBinding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply { |         feedBinding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply { | ||||||
|             spanSizeLookup = groupAdapter.spanSizeLookup |             spanSizeLookup = groupAdapter.spanSizeLookup | ||||||
|         } |         } | ||||||
| @@ -384,7 +383,7 @@ class FeedFragment : BaseStateFragment<FeedState>() { | |||||||
|     @SuppressLint("StringFormatMatches") |     @SuppressLint("StringFormatMatches") | ||||||
|     private fun handleLoadedState(loadedState: FeedState.LoadedState) { |     private fun handleLoadedState(loadedState: FeedState.LoadedState) { | ||||||
|  |  | ||||||
|         val itemVersion = if (shouldUseGridLayout()) { |         val itemVersion = if (shouldUseGridLayout(context)) { | ||||||
|             StreamItem.ItemVersion.GRID |             StreamItem.ItemVersion.GRID | ||||||
|         } else { |         } else { | ||||||
|             StreamItem.ItemVersion.NORMAL |             StreamItem.ItemVersion.NORMAL | ||||||
| @@ -528,35 +527,6 @@ class FeedFragment : BaseStateFragment<FeedState>() { | |||||||
|         listState = null |         listState = null | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // ///////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Grid Mode |  | ||||||
|     // ///////////////////////////////////////////////////////////////////////// |  | ||||||
|  |  | ||||||
|     // TODO: Move these out of this class, as it can be reused |  | ||||||
|  |  | ||||||
|     private fun shouldUseGridLayout(): Boolean { |  | ||||||
|         val listMode = PreferenceManager.getDefaultSharedPreferences(requireContext()) |  | ||||||
|             .getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value)) |  | ||||||
|  |  | ||||||
|         return when (listMode) { |  | ||||||
|             getString(R.string.list_view_mode_auto_key) -> { |  | ||||||
|                 val configuration = resources.configuration |  | ||||||
|  |  | ||||||
|                 ( |  | ||||||
|                     configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && |  | ||||||
|                         configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE) |  | ||||||
|                     ) |  | ||||||
|             } |  | ||||||
|             getString(R.string.list_view_mode_grid_key) -> true |  | ||||||
|             else -> false |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun getGridSpanCount(): Int { |  | ||||||
|         val minWidth = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width) |  | ||||||
|         return max(1, floor(resources.displayMetrics.widthPixels / minWidth.toDouble()).toInt()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         const val KEY_GROUP_ID = "ARG_GROUP_ID" |         const val KEY_GROUP_ID = "ARG_GROUP_ID" | ||||||
|         const val KEY_GROUP_NAME = "ARG_GROUP_NAME" |         const val KEY_GROUP_NAME = "ARG_GROUP_NAME" | ||||||
|   | |||||||
| @@ -68,6 +68,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject; | |||||||
|  |  | ||||||
| import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; | import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; | ||||||
| import static org.schabi.newpipe.ktx.ViewUtils.animate; | import static org.schabi.newpipe.ktx.ViewUtils.animate; | ||||||
|  | import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; | ||||||
|  |  | ||||||
| public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> { | public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> { | ||||||
|     // Save the list 10 seconds after the last change occurred |     // Save the list 10 seconds after the last change occurred | ||||||
| @@ -678,7 +679,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | |||||||
|  |  | ||||||
|     private ItemTouchHelper.SimpleCallback getItemTouchCallback() { |     private ItemTouchHelper.SimpleCallback getItemTouchCallback() { | ||||||
|         int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN; |         int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN; | ||||||
|         if (isGridLayout()) { |         if (shouldUseGridLayout(requireContext())) { | ||||||
|             directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; |             directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; | ||||||
|         } |         } | ||||||
|         return new ItemTouchHelper.SimpleCallback(directions, |         return new ItemTouchHelper.SimpleCallback(directions, | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ import android.content.Context | |||||||
| import android.content.DialogInterface | import android.content.DialogInterface | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.content.IntentFilter | import android.content.IntentFilter | ||||||
| import android.content.res.Configuration |  | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import android.os.Parcelable | import android.os.Parcelable | ||||||
| import android.view.LayoutInflater | import android.view.LayoutInflater | ||||||
| @@ -20,7 +19,6 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo | |||||||
| import androidx.appcompat.app.AlertDialog | import androidx.appcompat.app.AlertDialog | ||||||
| import androidx.lifecycle.ViewModelProvider | import androidx.lifecycle.ViewModelProvider | ||||||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager | import androidx.localbroadcastmanager.content.LocalBroadcastManager | ||||||
| import androidx.preference.PreferenceManager |  | ||||||
| import androidx.recyclerview.widget.GridLayoutManager | import androidx.recyclerview.widget.GridLayoutManager | ||||||
| import com.xwray.groupie.Group | import com.xwray.groupie.Group | ||||||
| import com.xwray.groupie.GroupAdapter | import com.xwray.groupie.GroupAdapter | ||||||
| @@ -60,12 +58,12 @@ import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService | |||||||
| import org.schabi.newpipe.streams.io.StoredFileHelper | import org.schabi.newpipe.streams.io.StoredFileHelper | ||||||
| import org.schabi.newpipe.util.NavigationHelper | import org.schabi.newpipe.util.NavigationHelper | ||||||
| import org.schabi.newpipe.util.OnClickGesture | import org.schabi.newpipe.util.OnClickGesture | ||||||
|  | import org.schabi.newpipe.util.ThemeHelper.getGridSpanCount | ||||||
|  | import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout | ||||||
| import org.schabi.newpipe.util.external_communication.ShareUtils | import org.schabi.newpipe.util.external_communication.ShareUtils | ||||||
| import java.text.SimpleDateFormat | import java.text.SimpleDateFormat | ||||||
| import java.util.Date | import java.util.Date | ||||||
| import java.util.Locale | import java.util.Locale | ||||||
| import kotlin.math.floor |  | ||||||
| import kotlin.math.max |  | ||||||
|  |  | ||||||
| class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | ||||||
|     private var _binding: FragmentSubscriptionBinding? = null |     private var _binding: FragmentSubscriptionBinding? = null | ||||||
| @@ -279,8 +277,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | |||||||
|         super.initViews(rootView, savedInstanceState) |         super.initViews(rootView, savedInstanceState) | ||||||
|         _binding = FragmentSubscriptionBinding.bind(rootView) |         _binding = FragmentSubscriptionBinding.bind(rootView) | ||||||
|  |  | ||||||
|         val shouldUseGridLayout = shouldUseGridLayout() |         groupAdapter.spanCount = if (shouldUseGridLayout(context)) getGridSpanCount(context) else 1 | ||||||
|         groupAdapter.spanCount = if (shouldUseGridLayout) getGridSpanCount() else 1 |  | ||||||
|         binding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply { |         binding.itemsList.layoutManager = GridLayoutManager(requireContext(), groupAdapter.spanCount).apply { | ||||||
|             spanSizeLookup = groupAdapter.spanSizeLookup |             spanSizeLookup = groupAdapter.spanSizeLookup | ||||||
|         } |         } | ||||||
| @@ -359,7 +356,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | |||||||
|     override fun handleResult(result: SubscriptionState) { |     override fun handleResult(result: SubscriptionState) { | ||||||
|         super.handleResult(result) |         super.handleResult(result) | ||||||
|  |  | ||||||
|         val shouldUseGridLayout = shouldUseGridLayout() |         val shouldUseGridLayout = shouldUseGridLayout(context) | ||||||
|         when (result) { |         when (result) { | ||||||
|             is SubscriptionState.LoadedState -> { |             is SubscriptionState.LoadedState -> { | ||||||
|                 result.subscriptions.forEach { |                 result.subscriptions.forEach { | ||||||
| @@ -420,30 +417,4 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | |||||||
|         super.hideLoading() |         super.hideLoading() | ||||||
|         binding.itemsList.animate(true, 200) |         binding.itemsList.animate(true, 200) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // ///////////////////////////////////////////////////////////////////////// |  | ||||||
|     // Grid Mode |  | ||||||
|     // ///////////////////////////////////////////////////////////////////////// |  | ||||||
|  |  | ||||||
|     // TODO: Move these out of this class, as it can be reused |  | ||||||
|  |  | ||||||
|     private fun shouldUseGridLayout(): Boolean { |  | ||||||
|         val listMode = PreferenceManager.getDefaultSharedPreferences(requireContext()) |  | ||||||
|             .getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value)) |  | ||||||
|  |  | ||||||
|         return when (listMode) { |  | ||||||
|             getString(R.string.list_view_mode_auto_key) -> { |  | ||||||
|                 val configuration = resources.configuration |  | ||||||
|                 configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && |  | ||||||
|                     configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE) |  | ||||||
|             } |  | ||||||
|             getString(R.string.list_view_mode_grid_key) -> true |  | ||||||
|             else -> false |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun getGridSpanCount(): Int { |  | ||||||
|         val minWidth = resources.getDimensionPixelSize(R.dimen.channel_item_grid_min_width) |  | ||||||
|         return max(1, floor(resources.displayMetrics.widthPixels / minWidth.toDouble()).toInt()) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,9 @@ import android.os.Build; | |||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
|  | import androidx.activity.result.ActivityResult; | ||||||
|  | import androidx.activity.result.ActivityResultLauncher; | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.StringRes; | import androidx.annotation.StringRes; | ||||||
| import androidx.appcompat.app.AlertDialog; | import androidx.appcompat.app.AlertDialog; | ||||||
| @@ -18,6 +21,7 @@ import androidx.preference.SwitchPreferenceCompat; | |||||||
| import com.nononsenseapps.filepicker.Utils; | import com.nononsenseapps.filepicker.Utils; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.streams.io.StoredDirectoryHelper; | ||||||
| import org.schabi.newpipe.util.FilePickerActivityHelper; | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| @@ -27,14 +31,10 @@ import java.net.URI; | |||||||
| import java.net.URLDecoder; | import java.net.URLDecoder; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.streams.io.StoredDirectoryHelper; |  | ||||||
|  |  | ||||||
| import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; | import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; | ||||||
|  |  | ||||||
| public class DownloadSettingsFragment extends BasePreferenceFragment { | public class DownloadSettingsFragment extends BasePreferenceFragment { | ||||||
|     public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true; |     public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true; | ||||||
|     private static final int REQUEST_DOWNLOAD_VIDEO_PATH = 0x1235; |  | ||||||
|     private static final int REQUEST_DOWNLOAD_AUDIO_PATH = 0x1236; |  | ||||||
|     private String downloadPathVideoPreference; |     private String downloadPathVideoPreference; | ||||||
|     private String downloadPathAudioPreference; |     private String downloadPathAudioPreference; | ||||||
|     private String storageUseSafPreference; |     private String storageUseSafPreference; | ||||||
| @@ -44,6 +44,12 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|     private Preference prefStorageAsk; |     private Preference prefStorageAsk; | ||||||
|  |  | ||||||
|     private Context ctx; |     private Context ctx; | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadVideoPathLauncher = | ||||||
|  |             registerForActivityResult( | ||||||
|  |                     new StartActivityForResult(), this::requestDownloadVideoPathResult); | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadAudioPathLauncher = | ||||||
|  |             registerForActivityResult( | ||||||
|  |                     new StartActivityForResult(), this::requestDownloadAudioPathResult); | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { |     public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { | ||||||
| @@ -185,7 +191,6 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         final String key = preference.getKey(); |         final String key = preference.getKey(); | ||||||
|         final int request; |  | ||||||
|  |  | ||||||
|         if (key.equals(storageUseSafPreference)) { |         if (key.equals(storageUseSafPreference)) { | ||||||
|             if (!NewPipeSettings.useStorageAccessFramework(ctx)) { |             if (!NewPipeSettings.useStorageAccessFramework(ctx)) { | ||||||
| @@ -198,43 +203,39 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|             updatePreferencesSummary(); |             updatePreferencesSummary(); | ||||||
|             return true; |             return true; | ||||||
|         } else if (key.equals(downloadPathVideoPreference)) { |         } else if (key.equals(downloadPathVideoPreference)) { | ||||||
|             request = REQUEST_DOWNLOAD_VIDEO_PATH; |             launchDirectoryPicker(requestDownloadVideoPathLauncher); | ||||||
|         } else if (key.equals(downloadPathAudioPreference)) { |         } else if (key.equals(downloadPathAudioPreference)) { | ||||||
|             request = REQUEST_DOWNLOAD_AUDIO_PATH; |             launchDirectoryPicker(requestDownloadAudioPathLauncher); | ||||||
|         } else { |         } else { | ||||||
|             return super.onPreferenceTreeClick(preference); |             return super.onPreferenceTreeClick(preference); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         startActivityForResult(StoredDirectoryHelper.getPicker(ctx), request); |  | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     private void launchDirectoryPicker(final ActivityResultLauncher<Intent> launcher) { | ||||||
|     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { |         launcher.launch(StoredDirectoryHelper.getPicker(ctx)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadVideoPathResult(final ActivityResult result) { | ||||||
|  |         requestDownloadPathResult(result, downloadPathVideoPreference); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadAudioPathResult(final ActivityResult result) { | ||||||
|  |         requestDownloadPathResult(result, downloadPathAudioPreference); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void requestDownloadPathResult(final ActivityResult result, final String key) { | ||||||
|         assureCorrectAppLanguage(getContext()); |         assureCorrectAppLanguage(getContext()); | ||||||
|         super.onActivityResult(requestCode, resultCode, data); |  | ||||||
|         if (DEBUG) { |  | ||||||
|             Log.d(TAG, "onActivityResult() called with: " |  | ||||||
|                     + "requestCode = [" + requestCode + "], " |  | ||||||
|                     + "resultCode = [" + resultCode + "], data = [" + data + "]" |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (resultCode != Activity.RESULT_OK) { |         if (result.getResultCode() != Activity.RESULT_OK) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         final String key; |         Uri uri = null; | ||||||
|         if (requestCode == REQUEST_DOWNLOAD_VIDEO_PATH) { |         if (result.getData() != null) { | ||||||
|             key = downloadPathVideoPreference; |             uri = result.getData().getData(); | ||||||
|         } else if (requestCode == REQUEST_DOWNLOAD_AUDIO_PATH) { |  | ||||||
|             key = downloadPathAudioPreference; |  | ||||||
|         } else { |  | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Uri uri = data.getData(); |  | ||||||
|         if (uri == null) { |         if (uri == null) { | ||||||
|             showMessageDialog(R.string.general_error, R.string.invalid_directory); |             showMessageDialog(R.string.general_error, R.string.invalid_directory); | ||||||
|             return; |             return; | ||||||
|   | |||||||
| @@ -298,4 +298,43 @@ public final class ThemeHelper { | |||||||
|             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); |             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether the grid layout or the list layout should be used. If the user set "auto" | ||||||
|  |      * mode in settings, decides based on screen orientation (landscape) and size. | ||||||
|  |      * | ||||||
|  |      * @param context the context to use | ||||||
|  |      * @return true:use grid layout, false:use list layout | ||||||
|  |      */ | ||||||
|  |     public static boolean shouldUseGridLayout(final Context context) { | ||||||
|  |         final String listMode = PreferenceManager.getDefaultSharedPreferences(context) | ||||||
|  |                 .getString(context.getString(R.string.list_view_mode_key), | ||||||
|  |                         context.getString(R.string.list_view_mode_value)); | ||||||
|  |  | ||||||
|  |         if (listMode.equals(context.getString(R.string.list_view_mode_list_key))) { | ||||||
|  |             return false; | ||||||
|  |         } else if (listMode.equals(context.getString(R.string.list_view_mode_grid_key))) { | ||||||
|  |             return true; | ||||||
|  |         } else { | ||||||
|  |             final Configuration configuration = context.getResources().getConfiguration(); | ||||||
|  |             return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE | ||||||
|  |                     && configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Calculates the number of grid items that can fit horizontally on the screen. The width of a | ||||||
|  |      * grid item is obtained from the thumbnail width plus the right and left paddings. | ||||||
|  |      * | ||||||
|  |      * @param context the context to use | ||||||
|  |      * @return the span count of grid list items | ||||||
|  |      */ | ||||||
|  |     public static int getGridSpanCount(final Context context) { | ||||||
|  |         final Resources res = context.getResources(); | ||||||
|  |         final int minWidth | ||||||
|  |                 = res.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width) | ||||||
|  |                 + res.getDimensionPixelSize(R.dimen.video_item_search_padding) * 2; | ||||||
|  |         return Math.max(1, res.getDisplayMetrics().widthPixels / minWidth); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,6 +17,9 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
|  |  | ||||||
|  | import androidx.activity.result.ActivityResult; | ||||||
|  | import androidx.activity.result.ActivityResultLauncher; | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.appcompat.app.AlertDialog; | import androidx.appcompat.app.AlertDialog; | ||||||
| import androidx.fragment.app.Fragment; | import androidx.fragment.app.Fragment; | ||||||
| @@ -44,7 +47,6 @@ 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_SAVE_AS = 0x1230; |  | ||||||
|  |  | ||||||
|     private SharedPreferences mPrefs; |     private SharedPreferences mPrefs; | ||||||
|     private boolean mLinear; |     private boolean mLinear; | ||||||
| @@ -64,7 +66,8 @@ public class MissionsFragment extends Fragment { | |||||||
|     private boolean mForceUpdate; |     private boolean mForceUpdate; | ||||||
|  |  | ||||||
|     private DownloadMission unsafeMissionTarget = null; |     private DownloadMission unsafeMissionTarget = null; | ||||||
|  |     private final ActivityResultLauncher<Intent> requestDownloadSaveAsLauncher = | ||||||
|  |             registerForActivityResult(new StartActivityForResult(), this::requestDownloadSaveAsResult); | ||||||
|     private final ServiceConnection mConnection = new ServiceConnection() { |     private final ServiceConnection mConnection = new ServiceConnection() { | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
| @@ -254,8 +257,9 @@ public class MissionsFragment extends Fragment { | |||||||
|             initialPath = Uri.parse(initialSavePath.getAbsolutePath()); |             initialPath = Uri.parse(initialSavePath.getAbsolutePath()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         startActivityForResult(StoredFileHelper.getNewPicker(mContext, mission.storage.getName(), |         requestDownloadSaveAsLauncher.launch( | ||||||
|                 mission.storage.getType(), initialPath), REQUEST_DOWNLOAD_SAVE_AS); |                 StoredFileHelper.getNewPicker(mContext, mission.storage.getName(), | ||||||
|  |                         mission.storage.getType(), initialPath)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -289,18 +293,17 @@ public class MissionsFragment extends Fragment { | |||||||
|         if (mBinder != null) mBinder.enableNotifications(true); |         if (mBinder != null) mBinder.enableNotifications(true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     private void requestDownloadSaveAsResult(final ActivityResult result) { | ||||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { |         if (result.getResultCode() != Activity.RESULT_OK) { | ||||||
|         super.onActivityResult(requestCode, resultCode, data); |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (requestCode != REQUEST_DOWNLOAD_SAVE_AS || resultCode != Activity.RESULT_OK) return; |         if (unsafeMissionTarget == null || result.getData() == null) { | ||||||
|  |  | ||||||
|         if (unsafeMissionTarget == null || data.getData() == null) { |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             Uri fileUri = data.getData(); |             Uri fileUri = result.getData().getData(); | ||||||
|             if (fileUri.getAuthority() != null && FilePickerActivityHelper.isOwnFileUri(mContext, fileUri)) { |             if (fileUri.getAuthority() != null && FilePickerActivityHelper.isOwnFileUri(mContext, fileUri)) { | ||||||
|                 fileUri = Uri.fromFile(Utils.getFileForUri(fileUri)); |                 fileUri = Uri.fromFile(Utils.getFileForUri(fileUri)); | ||||||
|             } |             } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								fastlane/metadata/android/en-US/changelogs/973.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								fastlane/metadata/android/en-US/changelogs/973.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | Hotfix | ||||||
|  | • Fix thumbnails and titles being trimmed in grid layout, due to a wrong calculation of how many videos can fit in one row | ||||||
|  | • Fix download dialog disappearing without doing anything if opened from the share menu | ||||||
|  | • Update a library related to opening external activities such as the Storage Access Framework file picker | ||||||
		Reference in New Issue
	
	Block a user
	 Tobi
					Tobi