Skip to content

Commit c3c39a7

Browse files
committed
1 parent 00770fc commit c3c39a7

3 files changed

Lines changed: 51 additions & 67 deletions

File tree

app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -859,20 +859,19 @@ private void prepareSelectedDownload() {
859859
return;
860860
}
861861

862-
// Check for free memory space (for api 24 and up)
863-
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
864-
final long freeSpace = mainStorage.getFreeMemory();
865-
if (freeSpace <= size) {
866-
Toast.makeText(context, getString(R.
867-
string.error_insufficient_storage), Toast.LENGTH_LONG).show();
868-
// move the user to storage setting tab
869-
final Intent storageSettingsIntent = new Intent(Settings.
870-
ACTION_INTERNAL_STORAGE_SETTINGS);
871-
if (storageSettingsIntent.resolveActivity(context.getPackageManager()) != null) {
872-
startActivity(storageSettingsIntent);
873-
}
874-
return;
862+
// Check for free storage space
863+
final long freeSpace = mainStorage.getFreeStorageSpace();
864+
if (freeSpace <= size) {
865+
Toast.makeText(context, getString(R.
866+
string.error_insufficient_storage), Toast.LENGTH_LONG).show();
867+
// move the user to storage setting tab
868+
final Intent storageSettingsIntent = new Intent(Settings.
869+
ACTION_INTERNAL_STORAGE_SETTINGS);
870+
if (storageSettingsIntent.resolveActivity(context.getPackageManager())
871+
!= null) {
872+
startActivity(storageSettingsIntent);
875873
}
874+
return;
876875
}
877876

878877
// check for existing file with the same name

app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
package org.schabi.newpipe.streams.io;
22

3+
import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME;
4+
import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID;
5+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
6+
37
import android.content.ContentResolver;
48
import android.content.Context;
59
import android.content.Intent;
610
import android.database.Cursor;
711
import android.net.Uri;
8-
import android.os.Build;
9-
import android.os.storage.StorageManager;
10-
import android.os.storage.StorageVolume;
12+
import android.os.ParcelFileDescriptor;
1113
import android.provider.DocumentsContract;
14+
import android.system.ErrnoException;
15+
import android.system.Os;
16+
import android.system.StructStatVfs;
1217
import android.util.Log;
1318

1419
import androidx.annotation.NonNull;
1520
import androidx.annotation.Nullable;
16-
import androidx.annotation.RequiresApi;
1721
import androidx.documentfile.provider.DocumentFile;
1822

1923
import org.schabi.newpipe.settings.NewPipeSettings;
2024
import org.schabi.newpipe.util.FilePickerActivityHelper;
2125

26+
import java.io.FileDescriptor;
2227
import java.io.IOException;
2328
import java.net.URI;
2429
import java.nio.file.Files;
@@ -27,16 +32,9 @@
2732
import java.util.ArrayList;
2833
import java.util.Collections;
2934
import java.util.List;
30-
import java.util.UUID;
3135
import java.util.stream.Collectors;
3236
import java.util.stream.Stream;
3337

34-
import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME;
35-
import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID;
36-
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
37-
38-
import us.shandian.giga.util.Utility;
39-
4038
public class StoredDirectoryHelper {
4139
private static final String TAG = StoredDirectoryHelper.class.getSimpleName();
4240
public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -45,6 +43,7 @@ public class StoredDirectoryHelper {
4543
private Path ioTree;
4644
private DocumentFile docTree;
4745

46+
// will be `null` for non-SAF files, i.e. files that use `ioTree`
4847
private Context context;
4948

5049
private final String tag;
@@ -176,41 +175,41 @@ public boolean isDirect() {
176175
}
177176

178177
/**
179-
* Get free memory of the storage partition (root of the directory).
180-
* @return amount of free memory in the volume of current directory (bytes)
178+
* Get free memory of the storage partition this file belongs to (root of the directory).
179+
* See <a href="https://stackoverflow.com/q/31171838">StackOverflow</a> and
180+
* <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html">
181+
* {@code statvfs()} and {@code fstatvfs()} docs</a>
182+
*
183+
* @return amount of free memory in the volume of current directory (bytes), or {@link
184+
* Long#MAX_VALUE} if an error occurred
181185
*/
182-
@RequiresApi(api = Build.VERSION_CODES.N) // Necessary for `getStorageVolume()`
183-
public long getFreeMemory() {
184-
final Uri uri = getUri();
185-
final StorageManager storageManager = (StorageManager) context.
186-
getSystemService(Context.STORAGE_SERVICE);
187-
final List<StorageVolume> volumes = storageManager.getStorageVolumes();
188-
189-
final String docId = DocumentsContract.getDocumentId(uri);
190-
final String[] split = docId.split(":");
191-
if (split.length > 0) {
192-
final String volumeId = split[0];
193-
194-
for (final StorageVolume volume : volumes) {
195-
// if the volume is an internal system volume
196-
if (volume.isPrimary() && volumeId.equalsIgnoreCase("primary")) {
197-
return Utility.getSystemFreeMemory();
198-
}
186+
public long getFreeStorageSpace() {
187+
try {
188+
final StructStatVfs stat;
199189

200-
// if the volume is a removable volume (normally an SD card)
201-
if (volume.isRemovable() && !volume.isPrimary()) {
202-
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
203-
try {
204-
final String sdCardUUID = volume.getUuid();
205-
return storageManager.getAllocatableBytes(UUID.fromString(sdCardUUID));
206-
} catch (final Exception e) {
207-
// do nothing
208-
}
190+
if (ioTree != null) {
191+
// non-SAF file, use statvfs with the path directly (also, `context` would be null
192+
// for non-SAF files, so we wouldn't be able to call `getContentResolver` anyway)
193+
stat = Os.statvfs(ioTree.toString());
194+
195+
} else {
196+
// SAF file, we can't get a path directly, so obtain a file descriptor first
197+
// and then use fstatvfs with the file descriptor
198+
try (ParcelFileDescriptor parcelFileDescriptor =
199+
context.getContentResolver().openFileDescriptor(getUri(), "r")) {
200+
if (parcelFileDescriptor == null) {
201+
return Long.MAX_VALUE;
209202
}
203+
final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
204+
stat = Os.fstatvfs(fileDescriptor);
210205
}
211206
}
207+
208+
// this is the same formula used inside the FsStat class
209+
return stat.f_bavail * stat.f_frsize;
210+
} catch (final IOException | ErrnoException e) {
211+
return Long.MAX_VALUE;
212212
}
213-
return Long.MAX_VALUE;
214213
}
215214

216215
/**

app/src/main/java/us/shandian/giga/util/Utility.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,6 @@ public enum FileType {
4040
UNKNOWN
4141
}
4242

43-
/**
44-
* Get amount of free system's memory.
45-
* @return free memory (bytes)
46-
*/
47-
public static long getSystemFreeMemory() {
48-
try {
49-
final StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
50-
return statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong();
51-
} catch (final Exception e) {
52-
// do nothing
53-
}
54-
return -1;
55-
}
56-
5743
public static String formatBytes(long bytes) {
5844
Locale locale = Locale.getDefault();
5945
if (bytes < 1024) {

0 commit comments

Comments
 (0)