Added "free memory" check before downloading [Android N / API 24+] (#10505)
Added "free space" check before downloading eliminating bugs related to out-of-memory on Android N / API 24+
This commit is contained in:
parent
5bdb6f18d6
commit
2e318b8b03
3 changed files with 82 additions and 4 deletions
|
@ -16,6 +16,7 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -147,7 +148,6 @@ public class DownloadDialog extends DialogFragment
|
|||
registerForActivityResult(
|
||||
new StartActivityForResult(), this::requestDownloadPickVideoFolderResult);
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Instance creation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -565,7 +565,6 @@ public class DownloadDialog extends DialogFragment
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Listeners
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -784,6 +783,7 @@ public class DownloadDialog extends DialogFragment
|
|||
final StoredDirectoryHelper mainStorage;
|
||||
final MediaFormat format;
|
||||
final String selectedMediaType;
|
||||
final long size;
|
||||
|
||||
// first, build the filename and get the output folder (if possible)
|
||||
// later, run a very very very large file checking logic
|
||||
|
@ -795,6 +795,7 @@ public class DownloadDialog extends DialogFragment
|
|||
selectedMediaType = getString(R.string.last_download_type_audio_key);
|
||||
mainStorage = mainStorageAudio;
|
||||
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
|
||||
size = getWrappedAudioStreams().getSizeInBytes(selectedAudioIndex);
|
||||
if (format == MediaFormat.WEBMA_OPUS) {
|
||||
mimeTmp = "audio/ogg";
|
||||
filenameTmp += "opus";
|
||||
|
@ -807,6 +808,7 @@ public class DownloadDialog extends DialogFragment
|
|||
selectedMediaType = getString(R.string.last_download_type_video_key);
|
||||
mainStorage = mainStorageVideo;
|
||||
format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat();
|
||||
size = wrappedVideoStreams.getSizeInBytes(selectedVideoIndex);
|
||||
if (format != null) {
|
||||
mimeTmp = format.mimeType;
|
||||
filenameTmp += format.getSuffix();
|
||||
|
@ -816,6 +818,7 @@ public class DownloadDialog extends DialogFragment
|
|||
selectedMediaType = getString(R.string.last_download_type_subtitle_key);
|
||||
mainStorage = mainStorageVideo; // subtitle & video files go together
|
||||
format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat();
|
||||
size = wrappedSubtitleStreams.getSizeInBytes(selectedSubtitleIndex);
|
||||
if (format != null) {
|
||||
mimeTmp = format.mimeType;
|
||||
}
|
||||
|
@ -871,6 +874,22 @@ public class DownloadDialog extends DialogFragment
|
|||
return;
|
||||
}
|
||||
|
||||
// Check for free memory space (for api 24 and up)
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
final long freeSpace = mainStorage.getFreeMemory();
|
||||
if (freeSpace <= size) {
|
||||
Toast.makeText(context, getString(R.
|
||||
string.error_insufficient_storage), Toast.LENGTH_LONG).show();
|
||||
// move the user to storage setting tab
|
||||
final Intent storageSettingsIntent = new Intent(Settings.
|
||||
ACTION_INTERNAL_STORAGE_SETTINGS);
|
||||
if (storageSettingsIntent.resolveActivity(context.getPackageManager()) != null) {
|
||||
startActivity(storageSettingsIntent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check for existing file with the same name
|
||||
checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp,
|
||||
mimeTmp);
|
||||
|
|
|
@ -5,11 +5,15 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.StorageVolume;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
|
@ -23,6 +27,7 @@ import java.nio.file.Paths;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -30,6 +35,8 @@ 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 {
|
||||
private static final String TAG = StoredDirectoryHelper.class.getSimpleName();
|
||||
public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
|
@ -168,6 +175,44 @@ public class StoredDirectoryHelper {
|
|||
return docTree == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get free memory of the storage partition (root of the directory).
|
||||
* @return amount of free memory in the volume of current directory (bytes)
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N) // Necessary for `getStorageVolume()`
|
||||
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 {
|
||||
final String sdCardUUID = volume.getUuid();
|
||||
return storageManager.getAllocatableBytes(UUID.fromString(sdCardUUID));
|
||||
} catch (final Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only using Java I/O. Creates the directory named by this abstract pathname, including any
|
||||
* necessary but nonexistent parent directories.
|
||||
|
|
|
@ -2,6 +2,8 @@ package us.shandian.giga.util;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
|
@ -26,10 +28,8 @@ import java.io.ObjectOutputStream;
|
|||
import java.io.Serializable;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
import okio.ByteString;
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
|
||||
public class Utility {
|
||||
|
||||
|
@ -40,6 +40,20 @@ public class Utility {
|
|||
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) {
|
||||
Locale locale = Locale.getDefault();
|
||||
if (bytes < 1024) {
|
||||
|
|
Loading…
Reference in a new issue