and more fixes

* fix content length reading
* use float overflow. Expensive, double is used instead
* fix invalid cast after click the mission body
* use a list for maximum attemps (downloads)
* minor clean up (DownloadManager.java)
* dont pass SharedPreferences instace to DownloadManager
* use a switch instead of checkbox for cross_network_downloads
* notify media scanner after deleting a finished download
This commit is contained in:
kapodamy 2018-11-24 00:14:37 -03:00
parent d647555e3a
commit f3d4d4747a
12 changed files with 111 additions and 52 deletions

View File

@ -404,11 +404,26 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
private int getSubtitleIndexBy(List<SubtitlesStream> streams) { private int getSubtitleIndexBy(List<SubtitlesStream> streams) {
Localization loc = NewPipe.getPreferredLocalization(); Localization loc = NewPipe.getPreferredLocalization();
for (int i = 0; i < streams.size(); i++) {
Locale streamLocale = streams.get(i).getLocale();
String tag = streamLocale.getLanguage().concat("-").concat(streamLocale.getCountry());
if (tag.equalsIgnoreCase(loc.getLanguage())) {
return i;
}
}
// fallback
// 1st loop match country & language
// 2nd loop match language only
String lang = loc.getLanguage().substring(0, loc.getLanguage().indexOf("-"));
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
for (int i = 0; i < streams.size(); i++) { for (int i = 0; i < streams.size(); i++) {
Locale streamLocale = streams.get(i).getLocale(); Locale streamLocale = streams.get(i).getLocale();
if (streamLocale.getLanguage().equals(loc.getLanguage())) {
if (j > 0 || streamLocale.getCountry().equals(loc.getCountry())) { if (streamLocale.getLanguage().equalsIgnoreCase(lang)) {
if (j > 0 || streamLocale.getCountry().equalsIgnoreCase(loc.getCountry())) {
return i; return i;
} }
} }

View File

@ -35,7 +35,9 @@ public class DownloadInitializer implements Runnable {
HttpURLConnection conn = mMission.openConnection(mId, -1, -1); HttpURLConnection conn = mMission.openConnection(mId, -1, -1);
if (!mMission.running || Thread.interrupted()) return; if (!mMission.running || Thread.interrupted()) return;
mMission.length = conn.getContentLength(); mMission.length = Utility.getContentLength(conn);
if (mMission.length == 0) { if (mMission.length == 0) {
mMission.notifyError(DownloadMission.ERROR_HTTP_NO_CONTENT, null); mMission.notifyError(DownloadMission.ERROR_HTTP_NO_CONTENT, null);
return; return;
@ -97,7 +99,7 @@ public class DownloadInitializer implements Runnable {
for (long i = 0; i < mMission.currentThreadCount; i++) { for (long i = 0; i < mMission.currentThreadCount; i++) {
mMission.threadBlockPositions.add(i); mMission.threadBlockPositions.add(i);
mMission.threadBytePositions.add(0); mMission.threadBytePositions.add(0L);
} }
File file; File file;

View File

@ -124,7 +124,7 @@ public class DownloadMission extends Mission {
@SuppressWarnings("UseSparseArrays")// LongSparseArray is not serializable @SuppressWarnings("UseSparseArrays")// LongSparseArray is not serializable
private final HashMap<Long, Boolean> blockState = new HashMap<>(); private final HashMap<Long, Boolean> blockState = new HashMap<>();
final List<Long> threadBlockPositions = new ArrayList<>(); final List<Long> threadBlockPositions = new ArrayList<>();
final List<Integer> threadBytePositions = new ArrayList<>(); final List<Long> threadBytePositions = new ArrayList<>();
private transient boolean deleted; private transient boolean deleted;
int currentThreadCount; int currentThreadCount;
@ -216,7 +216,7 @@ public class DownloadMission extends Mission {
* @param threadId the identifier of the thread * @param threadId the identifier of the thread
* @param position the relative position in bytes or zero * @param position the relative position in bytes or zero
*/ */
void setThreadBytePosition(int threadId, int position) { void setThreadBytePosition(int threadId, long position) {
threadBytePositions.set(threadId, position); threadBytePositions.set(threadId, position);
} }
@ -226,7 +226,7 @@ public class DownloadMission extends Mission {
* @param threadId the identifier of the thread * @param threadId the identifier of the thread
* @return the relative position in bytes or zero * @return the relative position in bytes or zero
*/ */
int getBlockBytePosition(int threadId) { long getBlockBytePosition(int threadId) {
return threadBytePositions.get(threadId); return threadBytePositions.get(threadId);
} }

View File

@ -89,7 +89,7 @@ public class DownloadRunnable implements Runnable {
end = mMission.length - 1; end = mMission.length - 1;
} }
int total = 0; long total = 0;
try { try {
HttpURLConnection conn = mMission.openConnection(mId, start, end); HttpURLConnection conn = mMission.openConnection(mId, start, end);

View File

@ -1,5 +1,6 @@
package us.shandian.giga.get; package us.shandian.giga.get;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
@ -10,9 +11,13 @@ import java.net.HttpURLConnection;
import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedByInterruptException;
import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG; import static org.schabi.newpipe.BuildConfig.DEBUG;
// Single-threaded fallback mode /**
* Single-threaded fallback mode
*/
public class DownloadRunnableFallback implements Runnable { public class DownloadRunnableFallback implements Runnable {
private static final String TAG = "DownloadRunnableFallback"; private static final String TAG = "DownloadRunnableFallback";
@ -43,10 +48,11 @@ public class DownloadRunnableFallback implements Runnable {
} }
@Override @Override
@SuppressLint("LongLogTag")
public void run() { public void run() {
boolean done; boolean done;
int start = 0; long start = 0;
if (!mMission.unknownLength) { if (!mMission.unknownLength) {
start = mMission.getBlockBytePosition(0); start = mMission.getBlockBytePosition(0);
@ -56,11 +62,12 @@ public class DownloadRunnableFallback implements Runnable {
} }
try { try {
int rangeStart = (mMission.unknownLength || start < 1) ? -1 : start; long rangeStart = (mMission.unknownLength || start < 1) ? -1 : start;
HttpURLConnection conn = mMission.openConnection(1, rangeStart, -1); HttpURLConnection conn = mMission.openConnection(1, rangeStart, -1);
// secondary check for the file length // secondary check for the file length
if (!mMission.unknownLength) mMission.unknownLength = conn.getContentLength() == -1; if (!mMission.unknownLength)
mMission.unknownLength = Utility.getContentLength(conn) == -1;
f = new RandomAccessFile(mMission.getDownloadedFile(), "rw"); f = new RandomAccessFile(mMission.getDownloadedFile(), "rw");
f.seek(mMission.offsets[mMission.current] + start); f.seek(mMission.offsets[mMission.current] + start);

View File

@ -1,9 +1,7 @@
package us.shandian.giga.service; package us.shandian.giga.service;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.util.DiffUtil; import android.support.v7.util.DiffUtil;
@ -46,9 +44,8 @@ public class DownloadManager {
private NetworkState mLastNetworkStatus = NetworkState.Unavailable; private NetworkState mLastNetworkStatus = NetworkState.Unavailable;
private SharedPreferences mPrefs; int mPrefMaxRetry;
private String mPrefMaxRetry; boolean mPrefCrossNetwork;
private String mPrefCrossNetwork;
/** /**
* Create a new instance * Create a new instance
@ -65,9 +62,6 @@ public class DownloadManager {
mHandler = handler; mHandler = handler;
mMissionsFinished = loadFinishedMissions(); mMissionsFinished = loadFinishedMissions();
mPendingMissionsDir = getPendingDir(context); mPendingMissionsDir = getPendingDir(context);
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
mPrefMaxRetry = context.getString(R.string.downloads_max_retry);
mPrefCrossNetwork = context.getString(R.string.cross_network_downloads);
if (!Utility.mkdir(mPendingMissionsDir, false)) { if (!Utility.mkdir(mPendingMissionsDir, false)) {
throw new RuntimeException("failed to create pending_downloads in data directory"); throw new RuntimeException("failed to create pending_downloads in data directory");
@ -196,17 +190,17 @@ public class DownloadManager {
/** /**
* Start a new download mission * Start a new download mission
* *
* @param urls the list of urls to download * @param urls the list of urls to download
* @param location the location * @param location the location
* @param name the name of the file to create * @param name the name of the file to create
* @param kind type of file (a: audio v: video s: subtitle ?: file-extension defined) * @param kind type of file (a: audio v: video s: subtitle ?: file-extension defined)
* @param threads the number of threads maximal used to download chunks of the file. * @param threads the number of threads maximal used to download chunks of the file.
* @param postprocessingName the name of the required post-processing algorithm, or {@code null} to ignore. * @param psName the name of the required post-processing algorithm, or {@code null} to ignore.
* @param source source url of the resource * @param source source url of the resource
* @param postProcessingArgs the arguments for the post-processing algorithm. * @param psArgs the arguments for the post-processing algorithm.
*/ */
void startMission(String[] urls, String location, String name, char kind, int threads, String source, void startMission(String[] urls, String location, String name, char kind, int threads,
String postprocessingName, String[] postProcessingArgs, long nearLength) { String source, String psName, String[] psArgs, long nearLength) {
synchronized (this) { synchronized (this) {
// check for existing pending download // check for existing pending download
DownloadMission pendingMission = getPendingMission(location, name); DownloadMission pendingMission = getPendingMission(location, name);
@ -225,12 +219,12 @@ public class DownloadManager {
if (index >= 0) mDownloadDataSource.deleteMission(mMissionsFinished.remove(index)); if (index >= 0) mDownloadDataSource.deleteMission(mMissionsFinished.remove(index));
} }
DownloadMission mission = new DownloadMission(urls, name, location, kind, postprocessingName, postProcessingArgs); DownloadMission mission = new DownloadMission(urls, name, location, kind, psName, psArgs);
mission.timestamp = System.currentTimeMillis(); mission.timestamp = System.currentTimeMillis();
mission.threadCount = threads; mission.threadCount = threads;
mission.source = source; mission.source = source;
mission.mHandler = mHandler; mission.mHandler = mHandler;
mission.maxRetry = mPrefs.getInt(mPrefMaxRetry, 3); mission.maxRetry = mPrefMaxRetry;
mission.nearLength = nearLength; mission.nearLength = nearLength;
while (true) { while (true) {
@ -420,6 +414,7 @@ public class DownloadManager {
/** /**
* runs another mission in queue if possible * runs another mission in queue if possible
*
* @return true if exits pending missions running or a mission was started, otherwise, false * @return true if exits pending missions running or a mission was started, otherwise, false
*/ */
boolean runAnotherMission() { boolean runAnotherMission() {
@ -460,18 +455,17 @@ public class DownloadManager {
private boolean canDownloadInCurrentNetwork() { private boolean canDownloadInCurrentNetwork() {
if (mLastNetworkStatus == NetworkState.Unavailable) return false; if (mLastNetworkStatus == NetworkState.Unavailable) return false;
return !(mPrefs.getBoolean(mPrefCrossNetwork, false) && mLastNetworkStatus == NetworkState.MobileOperating); return !(mPrefCrossNetwork && mLastNetworkStatus == NetworkState.MobileOperating);
} }
void handleConnectivityChange(NetworkState currentStatus) { void handleConnectivityChange(NetworkState currentStatus) {
if (currentStatus == mLastNetworkStatus) return; if (currentStatus == mLastNetworkStatus) return;
mLastNetworkStatus = currentStatus; mLastNetworkStatus = currentStatus;
boolean pauseOnMobile = mPrefs.getBoolean(mPrefCrossNetwork, false);
if (currentStatus == NetworkState.Unavailable) { if (currentStatus == NetworkState.Unavailable) {
return; return;
} else if (currentStatus != NetworkState.MobileOperating || !pauseOnMobile) { } else if (currentStatus != NetworkState.MobileOperating || !mPrefCrossNetwork) {
return; return;
} }
@ -488,9 +482,9 @@ public class DownloadManager {
if (flag) mHandler.sendEmptyMessage(DownloadManagerService.MESSAGE_PAUSED); if (flag) mHandler.sendEmptyMessage(DownloadManagerService.MESSAGE_PAUSED);
} }
void updateMaximumAttempts(int maxRetry) { void updateMaximumAttempts() {
synchronized (this) { synchronized (this) {
for (DownloadMission mission : mMissionsPending) mission.maxRetry = maxRetry; for (DownloadMission mission : mMissionsPending) mission.maxRetry = mPrefMaxRetry;
} }
} }

View File

@ -306,7 +306,12 @@ public class DownloadManagerService extends Service {
private void handlePreferenceChange(SharedPreferences prefs, String key) { private void handlePreferenceChange(SharedPreferences prefs, String key) {
if (key.equals(getString(R.string.downloads_max_retry))) { if (key.equals(getString(R.string.downloads_max_retry))) {
mManager.updateMaximumAttempts(prefs.getInt(key, 3)); mManager.mPrefMaxRetry = Integer.parseInt(
prefs.getString(key, getString(R.string.default_max_retry))
);
mManager.updateMaximumAttempts();
} else if (key.equals(getString(R.string.cross_network_downloads))) {
mManager.mPrefCrossNetwork = prefs.getBoolean(key, false);
} }
} }

View File

@ -183,6 +183,7 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> {
return mIterator.getSpecialAtItem(position); return mIterator.getSpecialAtItem(position);
} }
@SuppressLint("DefaultLocale")
private void updateProgress(ViewHolderItem h) { private void updateProgress(ViewHolderItem h) {
if (h == null || h.item == null || h.item.mission instanceof FinishedMission) return; if (h == null || h.item == null || h.item.mission instanceof FinishedMission) return;
@ -216,14 +217,15 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> {
progress = Float.NaN; progress = Float.NaN;
h.progress.setProgress(0f); h.progress.setProgress(0f);
} else { } else {
progress = (float) mission.done / mission.length; progress = (float) ((double) mission.done / mission.length);
if (mission.urls.length > 1 && mission.current < mission.urls.length) { if (mission.urls.length > 1 && mission.current < mission.urls.length) {
progress = (progress / mission.urls.length) + ((float) mission.current / mission.urls.length); progress = (progress / mission.urls.length) + ((float) mission.current / mission.urls.length);
} }
} }
if (hasError) { if (hasError) {
if (Float.isNaN(progress) || Float.isInfinite(progress)) h.progress.setProgress(1f); if (Float.isNaN(progress) || Float.isInfinite(progress))
h.progress.setProgress(1f);
h.status.setText(R.string.msg_error); h.status.setText(R.string.msg_error);
} else if (Float.isNaN(progress) || Float.isInfinite(progress)) { } else if (Float.isNaN(progress) || Float.isInfinite(progress)) {
h.status.setText("--.-%"); h.status.setText("--.-%");
@ -275,7 +277,7 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> {
if (deltaTime > 1000 && deltaDone > 0) { if (deltaTime > 1000 && deltaDone > 0) {
float speed = (float) deltaDone / deltaTime; float speed = (float) ((double) deltaDone / deltaTime);
String speedStr = Utility.formatSpeed(speed * 1000); String speedStr = Utility.formatSpeed(speed * 1000);
String sizeStr = Utility.formatBytes(length); String sizeStr = Utility.formatBytes(length);
@ -497,7 +499,7 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> {
mIterator.start(); mIterator.start();
mIterator.end(); mIterator.end();
for (ViewHolderItem item: mPendingDownloadsItems) { for (ViewHolderItem item : mPendingDownloadsItems) {
item.lastTimeStamp = -1; item.lastTimeStamp = -1;
} }
@ -592,11 +594,9 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> {
checksum = menu.findItem(R.id.checksum); checksum = menu.findItem(R.id.checksum);
itemView.setOnClickListener((v) -> { itemView.setOnClickListener((v) -> {
if (((DownloadMission) item.mission).isFinished()) if (item.mission instanceof FinishedMission)
viewWithFileProvider(item.mission.getDownloadedFile()); viewWithFileProvider(item.mission.getDownloadedFile());
}); });
//h.itemView.setOnClickListener(v -> showDetail(h));
} }
private void showPopupMenu() { private void showPopupMenu() {

View File

@ -1,7 +1,9 @@
package us.shandian.giga.ui.common; package us.shandian.giga.ui.common;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -11,6 +13,7 @@ import org.schabi.newpipe.R;
import java.util.ArrayList; import java.util.ArrayList;
import us.shandian.giga.get.FinishedMission;
import us.shandian.giga.get.Mission; import us.shandian.giga.get.Mission;
import us.shandian.giga.service.DownloadManager; import us.shandian.giga.service.DownloadManager;
import us.shandian.giga.service.DownloadManager.MissionIterator; import us.shandian.giga.service.DownloadManager.MissionIterator;
@ -120,6 +123,10 @@ public class Deleter {
mIterator.unHide(mission); mIterator.unHide(mission);
mDownloadManager.deleteMission(mission); mDownloadManager.deleteMission(mission);
if (mission instanceof FinishedMission) {
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(mission.getDownloadedFile())));
}
break; break;
} }

View File

@ -3,6 +3,7 @@ package us.shandian.giga.util;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -21,6 +22,7 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.net.HttpURLConnection;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Locale; import java.util.Locale;
@ -38,11 +40,11 @@ public class Utility {
if (bytes < 1024) { if (bytes < 1024) {
return String.format("%d B", bytes); return String.format("%d B", bytes);
} else if (bytes < 1024 * 1024) { } else if (bytes < 1024 * 1024) {
return String.format("%.2f kB", (float) bytes / 1024); return String.format("%.2f kB", bytes / 1024d);
} else if (bytes < 1024 * 1024 * 1024) { } else if (bytes < 1024 * 1024 * 1024) {
return String.format("%.2f MB", (float) bytes / 1024 / 1024); return String.format("%.2f MB", bytes / 1024d / 1024d);
} else { } else {
return String.format("%.2f GB", (float) bytes / 1024 / 1024 / 1024); return String.format("%.2f GB", bytes / 1024d / 1024d / 1024d);
} }
} }
@ -255,4 +257,19 @@ public class Utility {
return path.exists(); return path.exists();
} }
public static long getContentLength(HttpURLConnection connection) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return connection.getContentLengthLong();
}
try {
long length = Long.parseLong(connection.getHeaderField("Content-Length"));
if (length >= 0) return length;
} catch (Exception err) {
// nothing to do
}
return -1;
}
} }

View File

@ -176,6 +176,17 @@
<string name="default_file_charset_value" translatable="false">@string/charset_most_special_characters_value</string> <string name="default_file_charset_value" translatable="false">@string/charset_most_special_characters_value</string>
<string name="downloads_max_retry" translatable="false">downloads_max_retry</string> <string name="downloads_max_retry" translatable="false">downloads_max_retry</string>
<string-array name="downloads_max_retry_list" translatable="false">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>7</item>
<item>10</item>
<item>15</item>
</string-array>
<string name="default_max_retry" translatable="false">3</string> <string name="default_max_retry" translatable="false">3</string>
<string name="cross_network_downloads" translatable="false">cross_network_downloads</string> <string name="cross_network_downloads" translatable="false">cross_network_downloads</string>

View File

@ -29,14 +29,15 @@
android:summary="@string/settings_file_replacement_character_summary" android:summary="@string/settings_file_replacement_character_summary"
android:title="@string/settings_file_replacement_character_title"/> android:title="@string/settings_file_replacement_character_title"/>
<SeekBarPreference <ListPreference
android:defaultValue="@string/default_max_retry" android:defaultValue="@string/default_max_retry"
android:entries="@array/downloads_max_retry_list"
android:entryValues="@array/downloads_max_retry_list"
android:key="@string/downloads_max_retry" android:key="@string/downloads_max_retry"
android:max="15"
android:summary="@string/max_retry_desc" android:summary="@string/max_retry_desc"
android:title="@string/max_retry_msg" /> android:title="@string/max_retry_msg" />
<CheckBoxPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:key="@string/cross_network_downloads" android:key="@string/cross_network_downloads"
android:summary="@string/pause_downloads_on_mobile_desc" android:summary="@string/pause_downloads_on_mobile_desc"