mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Fix free storage space check for all APIs
See https://stackoverflow.com/q/31171838 See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html
This commit is contained in:
		| @@ -859,21 +859,20 @@ public class DownloadDialog extends DialogFragment | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Check for free memory space (for api 24 and up) |         // Check for free storage space | ||||||
|         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { |         final long freeSpace = mainStorage.getFreeStorageSpace(); | ||||||
|             final long freeSpace = mainStorage.getFreeMemory(); |  | ||||||
|         if (freeSpace <= size) { |         if (freeSpace <= size) { | ||||||
|             Toast.makeText(context, getString(R. |             Toast.makeText(context, getString(R. | ||||||
|                     string.error_insufficient_storage), Toast.LENGTH_LONG).show(); |                     string.error_insufficient_storage), Toast.LENGTH_LONG).show(); | ||||||
|             // move the user to storage setting tab |             // move the user to storage setting tab | ||||||
|             final Intent storageSettingsIntent = new Intent(Settings. |             final Intent storageSettingsIntent = new Intent(Settings. | ||||||
|                     ACTION_INTERNAL_STORAGE_SETTINGS); |                     ACTION_INTERNAL_STORAGE_SETTINGS); | ||||||
|                 if (storageSettingsIntent.resolveActivity(context.getPackageManager()) != null) { |             if (storageSettingsIntent.resolveActivity(context.getPackageManager()) | ||||||
|  |                     != null) { | ||||||
|                 startActivity(storageSettingsIntent); |                 startActivity(storageSettingsIntent); | ||||||
|             } |             } | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // check for existing file with the same name |         // check for existing file with the same name | ||||||
|         checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp, |         checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp, | ||||||
|   | |||||||
| @@ -1,24 +1,29 @@ | |||||||
| package org.schabi.newpipe.streams.io; | package org.schabi.newpipe.streams.io; | ||||||
|  |  | ||||||
|  | import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME; | ||||||
|  | import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID; | ||||||
|  | import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; | ||||||
|  |  | ||||||
| import android.content.ContentResolver; | import android.content.ContentResolver; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.database.Cursor; | import android.database.Cursor; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
| import android.os.Build; | import android.os.ParcelFileDescriptor; | ||||||
| import android.os.storage.StorageManager; |  | ||||||
| import android.os.storage.StorageVolume; |  | ||||||
| import android.provider.DocumentsContract; | import android.provider.DocumentsContract; | ||||||
|  | import android.system.ErrnoException; | ||||||
|  | import android.system.Os; | ||||||
|  | import android.system.StructStatVfs; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| import androidx.annotation.RequiresApi; |  | ||||||
| import androidx.documentfile.provider.DocumentFile; | import androidx.documentfile.provider.DocumentFile; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.settings.NewPipeSettings; | import org.schabi.newpipe.settings.NewPipeSettings; | ||||||
| import org.schabi.newpipe.util.FilePickerActivityHelper; | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
|  |  | ||||||
|  | import java.io.FileDescriptor; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| @@ -27,16 +32,9 @@ import java.nio.file.Paths; | |||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.UUID; |  | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| import java.util.stream.Stream; | import java.util.stream.Stream; | ||||||
|  |  | ||||||
| import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME; |  | ||||||
| import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID; |  | ||||||
| import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; |  | ||||||
|  |  | ||||||
| import us.shandian.giga.util.Utility; |  | ||||||
|  |  | ||||||
| public class StoredDirectoryHelper { | public class StoredDirectoryHelper { | ||||||
|     private static final String TAG = StoredDirectoryHelper.class.getSimpleName(); |     private static final String TAG = StoredDirectoryHelper.class.getSimpleName(); | ||||||
|     public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION |     public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION | ||||||
| @@ -45,6 +43,7 @@ public class StoredDirectoryHelper { | |||||||
|     private Path ioTree; |     private Path ioTree; | ||||||
|     private DocumentFile docTree; |     private DocumentFile docTree; | ||||||
|  |  | ||||||
|  |     // will be `null` for non-SAF files, i.e. files that use `ioTree` | ||||||
|     private Context context; |     private Context context; | ||||||
|  |  | ||||||
|     private final String tag; |     private final String tag; | ||||||
| @@ -176,42 +175,42 @@ public class StoredDirectoryHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Get free memory of the storage partition (root of the directory). |      * Get free memory of the storage partition this file belongs to (root of the directory). | ||||||
|      * @return amount of free memory in the volume of current directory (bytes) |      * See <a href="https://stackoverflow.com/q/31171838">StackOverflow</a> and | ||||||
|  |      * <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html"> | ||||||
|  |      *     {@code statvfs()} and {@code fstatvfs()} docs</a> | ||||||
|  |      * | ||||||
|  |      * @return amount of free memory in the volume of current directory (bytes), or {@link | ||||||
|  |      * Long#MAX_VALUE} if an error occurred | ||||||
|      */ |      */ | ||||||
|     @RequiresApi(api = Build.VERSION_CODES.N)  // Necessary for `getStorageVolume()` |     public long getFreeStorageSpace() { | ||||||
|     public long getFreeMemory() { |  | ||||||
|         final Uri uri = getUri(); |  | ||||||
|         final StorageManager storageManager = (StorageManager) context. |  | ||||||
|                 getSystemService(Context.STORAGE_SERVICE); |  | ||||||
|         final List<StorageVolume> volumes = storageManager.getStorageVolumes(); |  | ||||||
|  |  | ||||||
|         final String docId = DocumentsContract.getDocumentId(uri); |  | ||||||
|         final String[] split = docId.split(":"); |  | ||||||
|         if (split.length > 0) { |  | ||||||
|             final String volumeId = split[0]; |  | ||||||
|  |  | ||||||
|             for (final StorageVolume volume : volumes) { |  | ||||||
|                 // if the volume is an internal system volume |  | ||||||
|                 if (volume.isPrimary() && volumeId.equalsIgnoreCase("primary")) { |  | ||||||
|                     return Utility.getSystemFreeMemory(); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // if the volume is a removable volume (normally an SD card) |  | ||||||
|                 if (volume.isRemovable() && !volume.isPrimary()) { |  | ||||||
|                     if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { |  | ||||||
|         try { |         try { | ||||||
|                             final String sdCardUUID = volume.getUuid(); |             final StructStatVfs stat; | ||||||
|                             return storageManager.getAllocatableBytes(UUID.fromString(sdCardUUID)); |  | ||||||
|                         } catch (final Exception e) { |             if (ioTree != null) { | ||||||
|                             // do nothing |                 // non-SAF file, use statvfs with the path directly (also, `context` would be null | ||||||
|                         } |                 // for non-SAF files, so we wouldn't be able to call `getContentResolver` anyway) | ||||||
|                     } |                 stat = Os.statvfs(ioTree.toString()); | ||||||
|                 } |  | ||||||
|             } |             } else { | ||||||
|         } |                 // SAF file, we can't get a path directly, so obtain a file descriptor first | ||||||
|  |                 // and then use fstatvfs with the file descriptor | ||||||
|  |                 try (ParcelFileDescriptor parcelFileDescriptor = | ||||||
|  |                              context.getContentResolver().openFileDescriptor(getUri(), "r")) { | ||||||
|  |                     if (parcelFileDescriptor == null) { | ||||||
|                         return Long.MAX_VALUE; |                         return Long.MAX_VALUE; | ||||||
|                     } |                     } | ||||||
|  |                     final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); | ||||||
|  |                     stat = Os.fstatvfs(fileDescriptor); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // this is the same formula used inside the FsStat class | ||||||
|  |             return stat.f_bavail * stat.f_frsize; | ||||||
|  |         } catch (final IOException | ErrnoException e) { | ||||||
|  |             return Long.MAX_VALUE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Only using Java I/O. Creates the directory named by this abstract pathname, including any |      * Only using Java I/O. Creates the directory named by this abstract pathname, including any | ||||||
|   | |||||||
| @@ -40,20 +40,6 @@ public class Utility { | |||||||
|         UNKNOWN |         UNKNOWN | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Get amount of free system's memory. |  | ||||||
|      * @return free memory (bytes) |  | ||||||
|      */ |  | ||||||
|     public static long getSystemFreeMemory() { |  | ||||||
|         try { |  | ||||||
|             final StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); |  | ||||||
|             return statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong(); |  | ||||||
|         } catch (final Exception e) { |  | ||||||
|             // do nothing |  | ||||||
|         } |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static String formatBytes(long bytes) { |     public static String formatBytes(long bytes) { | ||||||
|         Locale locale = Locale.getDefault(); |         Locale locale = Locale.getDefault(); | ||||||
|         if (bytes < 1024) { |         if (bytes < 1024) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stypox
					Stypox