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
|
@ -16,6 +16,7 @@ import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -147,7 +148,6 @@ public class DownloadDialog extends DialogFragment
|
||||||
registerForActivityResult(
|
registerForActivityResult(
|
||||||
new StartActivityForResult(), this::requestDownloadPickVideoFolderResult);
|
new StartActivityForResult(), this::requestDownloadPickVideoFolderResult);
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Instance creation
|
// Instance creation
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -565,7 +565,6 @@ public class DownloadDialog extends DialogFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Listeners
|
// Listeners
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -784,6 +783,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
final StoredDirectoryHelper mainStorage;
|
final StoredDirectoryHelper mainStorage;
|
||||||
final MediaFormat format;
|
final MediaFormat format;
|
||||||
final String selectedMediaType;
|
final String selectedMediaType;
|
||||||
|
final long size;
|
||||||
|
|
||||||
// first, build the filename and get the output folder (if possible)
|
// first, build the filename and get the output folder (if possible)
|
||||||
// later, run a very very very large file checking logic
|
// 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);
|
selectedMediaType = getString(R.string.last_download_type_audio_key);
|
||||||
mainStorage = mainStorageAudio;
|
mainStorage = mainStorageAudio;
|
||||||
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
|
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
|
||||||
|
size = getWrappedAudioStreams().getSizeInBytes(selectedAudioIndex);
|
||||||
if (format == MediaFormat.WEBMA_OPUS) {
|
if (format == MediaFormat.WEBMA_OPUS) {
|
||||||
mimeTmp = "audio/ogg";
|
mimeTmp = "audio/ogg";
|
||||||
filenameTmp += "opus";
|
filenameTmp += "opus";
|
||||||
|
@ -807,6 +808,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
selectedMediaType = getString(R.string.last_download_type_video_key);
|
selectedMediaType = getString(R.string.last_download_type_video_key);
|
||||||
mainStorage = mainStorageVideo;
|
mainStorage = mainStorageVideo;
|
||||||
format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat();
|
format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat();
|
||||||
|
size = wrappedVideoStreams.getSizeInBytes(selectedVideoIndex);
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
mimeTmp = format.mimeType;
|
mimeTmp = format.mimeType;
|
||||||
filenameTmp += format.getSuffix();
|
filenameTmp += format.getSuffix();
|
||||||
|
@ -816,6 +818,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
selectedMediaType = getString(R.string.last_download_type_subtitle_key);
|
selectedMediaType = getString(R.string.last_download_type_subtitle_key);
|
||||||
mainStorage = mainStorageVideo; // subtitle & video files go together
|
mainStorage = mainStorageVideo; // subtitle & video files go together
|
||||||
format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat();
|
format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat();
|
||||||
|
size = wrappedSubtitleStreams.getSizeInBytes(selectedSubtitleIndex);
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
mimeTmp = format.mimeType;
|
mimeTmp = format.mimeType;
|
||||||
}
|
}
|
||||||
|
@ -871,6 +874,22 @@ public class DownloadDialog extends DialogFragment
|
||||||
return;
|
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
|
// check for existing file with the same name
|
||||||
checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp,
|
checkSelectedDownload(mainStorage, mainStorage.findFile(filenameTmp), filenameTmp,
|
||||||
mimeTmp);
|
mimeTmp);
|
||||||
|
|
|
@ -5,11 +5,15 @@ 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.storage.StorageManager;
|
||||||
|
import android.os.storage.StorageVolume;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
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;
|
||||||
|
@ -23,6 +27,7 @@ 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;
|
||||||
|
|
||||||
|
@ -30,6 +35,8 @@ import static android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME;
|
||||||
import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID;
|
import static android.provider.DocumentsContract.Root.COLUMN_DOCUMENT_ID;
|
||||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
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
|
||||||
|
@ -168,6 +175,44 @@ public class StoredDirectoryHelper {
|
||||||
return docTree == null;
|
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
|
* Only using Java I/O. Creates the directory named by this abstract pathname, including any
|
||||||
* necessary but nonexistent parent directories.
|
* necessary but nonexistent parent directories.
|
||||||
|
|
|
@ -2,6 +2,8 @@ package us.shandian.giga.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.StatFs;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
|
@ -26,10 +28,8 @@ import java.io.ObjectOutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import okio.ByteString;
|
import okio.ByteString;
|
||||||
import us.shandian.giga.get.DownloadMission;
|
|
||||||
|
|
||||||
public class Utility {
|
public class Utility {
|
||||||
|
|
||||||
|
@ -40,6 +40,20 @@ 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) {
|
||||||
|
|
Loading…
Reference in New Issue