Code improvements
* Replace unchecked casts with checked casts * remove Utility.finViewById * Fix return activity checking * Create UserAction enum * Fix typos * Add instrumented test for error info * ErrorInfo make fields final * Log exception using logger * Add inherited annotations * Resolve deprecation warnings * Remove unused methods from utility * Reformat code * Remove unused methods from Utility and improve getFileExt * Create OnScrollBelowItemsListener
This commit is contained in:
parent
40213b2d6a
commit
b03723c3fb
40 changed files with 2077 additions and 1981 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -5,7 +5,7 @@ READ THIS GUIDELINES CAREFULLY BEFORE CONTRIBUTING.
|
||||||
|
|
||||||
## Crash reporting
|
## Crash reporting
|
||||||
|
|
||||||
Do not report crashes in the GitHub issue tracker. NewPipe has an automated crash report system that will ask you to send a report if a crash occures.
|
Do not report crashes in the GitHub issue tracker. NewPipe has an automated crash report system that will ask you to send a report if a crash occurs.
|
||||||
|
|
||||||
## Issue reporting/feature request
|
## Issue reporting/feature request
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ android {
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 35
|
versionCode 35
|
||||||
versionName "0.9.8"
|
versionName "0.9.8"
|
||||||
|
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -31,6 +33,11 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
|
||||||
|
exclude module: 'support-annotations'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
testCompile 'org.json:json:20160810'
|
testCompile 'org.json:json:20160810'
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.support.test.filters.LargeTest;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity.ErrorInfo;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented tests for {@link ErrorInfo}
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@LargeTest
|
||||||
|
public class ErrorInfoTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void errorInfo_testParcelable() {
|
||||||
|
ErrorInfo info = ErrorInfo.make(UserAction.USER_REPORT, "youtube", "request", R.string.general_error);
|
||||||
|
// Obtain a Parcel object and write the parcelable object to it:
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
info.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
ErrorInfo infoFromParcel = ErrorInfo.CREATOR.createFromParcel(parcel);
|
||||||
|
|
||||||
|
assertEquals(UserAction.USER_REPORT, infoFromParcel.userAction);
|
||||||
|
assertEquals("youtube", infoFromParcel.serviceName);
|
||||||
|
assertEquals("request", infoFromParcel.request);
|
||||||
|
assertEquals(R.string.general_error, infoFromParcel.message);
|
||||||
|
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import org.acra.sender.ReportSenderFactory;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ public class App extends Application {
|
||||||
} catch(ACRAConfigurationException ace) {
|
} catch(ACRAConfigurationException ace) {
|
||||||
ace.printStackTrace();
|
ace.printStackTrace();
|
||||||
ErrorActivity.reportError(this, ace, null, null,
|
ErrorActivity.reportError(this, ace, null, null,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,"none",
|
ErrorActivity.ErrorInfo.make(UserAction.SEARCHED,"none",
|
||||||
"Could not initialize ACRA crash report", R.string.app_ui_crash));
|
"Could not initialize ACRA crash report", R.string.app_ui_crash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
|
@ -49,7 +50,7 @@ public class ImageErrorLoadingListener implements ImageLoadingListener {
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(context,
|
ErrorActivity.reportError(context,
|
||||||
failReason.getCause(), null, rootView,
|
failReason.getCause(), null, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE,
|
||||||
NewPipe.getNameOfService(serviceId), imageUri,
|
NewPipe.getNameOfService(serviceId), imageUri,
|
||||||
R.string.could_not_load_image));
|
R.string.could_not_load_image));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import us.shandian.giga.service.DownloadManagerService;
|
||||||
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
||||||
import us.shandian.giga.ui.fragment.MissionsFragment;
|
import us.shandian.giga.ui.fragment.MissionsFragment;
|
||||||
import us.shandian.giga.util.CrashHandler;
|
import us.shandian.giga.util.CrashHandler;
|
||||||
import us.shandian.giga.util.Utility;
|
|
||||||
|
|
||||||
public class DownloadActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
public class DownloadActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
|
@ -125,11 +124,11 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||||
// Create the view
|
// Create the view
|
||||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
View v = inflater.inflate(R.layout.dialog_url, null);
|
View v = inflater.inflate(R.layout.dialog_url, null);
|
||||||
final EditText name = Utility.findViewById(v, R.id.file_name);
|
final EditText name = (EditText) v.findViewById(R.id.file_name);
|
||||||
final TextView tCount = Utility.findViewById(v, R.id.threads_count);
|
final TextView tCount = (TextView) v.findViewById(R.id.threads_count);
|
||||||
final SeekBar threads = Utility.findViewById(v, R.id.threads);
|
final SeekBar threads = (SeekBar) v.findViewById(R.id.threads);
|
||||||
final Toolbar toolbar = Utility.findViewById(v, R.id.toolbar);
|
final Toolbar toolbar = (Toolbar) v.findViewById(R.id.toolbar);
|
||||||
final RadioButton audioButton = (RadioButton) Utility.findViewById(v, R.id.audio_button);
|
final RadioButton audioButton = (RadioButton) v.findViewById(R.id.audio_button);
|
||||||
|
|
||||||
|
|
||||||
threads.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
threads.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
|
|
@ -253,7 +253,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #143 #44 #42 #22: make shure that the filename does not contain illegal chars.
|
* #143 #44 #42 #22: make sure that the filename does not contain illegal chars.
|
||||||
* This should fix some of the "cannot download" problems.
|
* This should fix some of the "cannot download" problems.
|
||||||
*/
|
*/
|
||||||
private String createFileName(String fileName) {
|
private String createFileName(String fileName) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
import org.schabi.newpipe.fragments.BaseFragment;
|
import org.schabi.newpipe.fragments.BaseFragment;
|
||||||
|
import org.schabi.newpipe.fragments.search.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
@ -245,23 +246,12 @@ public class ChannelFragment extends BaseFragment implements ChannelExtractorWor
|
||||||
});
|
});
|
||||||
|
|
||||||
channelVideosList.clearOnScrollListeners();
|
channelVideosList.clearOnScrollListeners();
|
||||||
channelVideosList.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
channelVideosList.addOnScrollListener(new OnScrollBelowItemsListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
public void onScrolledDown(RecyclerView recyclerView) {
|
||||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
if ((currentChannelWorker == null || !currentChannelWorker.isRunning()) && hasNextPage && !isLoading.get()) {
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
pageNumber++;
|
||||||
//check for scroll down
|
loadMoreVideos();
|
||||||
if (dy > 0) {
|
|
||||||
LinearLayoutManager layoutManager = (LinearLayoutManager) channelVideosList.getLayoutManager();
|
|
||||||
|
|
||||||
visibleItemCount = layoutManager.getChildCount();
|
|
||||||
totalItemCount = layoutManager.getItemCount();
|
|
||||||
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
|
||||||
|
|
||||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && (currentChannelWorker == null || !currentChannelWorker.isRunning()) && hasNextPage && !isLoading.get()) {
|
|
||||||
pageNumber++;
|
|
||||||
loadMoreVideos();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
import org.schabi.newpipe.player.PlayVideoActivity;
|
import org.schabi.newpipe.player.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
@ -578,7 +579,7 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
|
||||||
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, displayImageOptions, new SimpleImageLoadingListener() {
|
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView, displayImageOptions, new SimpleImageLoadingListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE, NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
|
ErrorActivity.reportError(activity, failReason.getCause(), null, activity.findViewById(android.R.id.content), ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE, NewPipe.getNameOfService(currentStreamInfo.service_id), imageUri, R.string.could_not_load_thumbnails));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
|
} else thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recycler view scroll listener which calls the method {@link #onScrolledDown(RecyclerView)}
|
||||||
|
* if the view is scrolled below the last item.
|
||||||
|
*/
|
||||||
|
public abstract class OnScrollBelowItemsListener extends RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
//check for scroll down
|
||||||
|
if (dy > 0) {
|
||||||
|
int pastVisibleItems, visibleItemCount, totalItemCount;
|
||||||
|
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||||
|
visibleItemCount = recyclerView.getLayoutManager().getChildCount();
|
||||||
|
totalItemCount = recyclerView.getLayoutManager().getItemCount();
|
||||||
|
pastVisibleItems = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
|
||||||
|
onScrolledDown(recyclerView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the recycler view is scrolled below the last item.
|
||||||
|
* @param recyclerView the recycler view
|
||||||
|
*/
|
||||||
|
public abstract void onScrolledDown(RecyclerView recyclerView);
|
||||||
|
}
|
|
@ -242,33 +242,24 @@ public class SearchFragment extends BaseFragment implements SuggestionWorker.OnS
|
||||||
protected void initListeners() {
|
protected void initListeners() {
|
||||||
super.initListeners();
|
super.initListeners();
|
||||||
resultRecyclerView.clearOnScrollListeners();
|
resultRecyclerView.clearOnScrollListeners();
|
||||||
resultRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
resultRecyclerView.addOnScrollListener(new OnScrollBelowItemsListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
public void onScrolledDown(RecyclerView recyclerView) {
|
||||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
if(!isLoading.get()) {
|
||||||
super.onScrolled(recyclerView, dx, dy);
|
pageNumber++;
|
||||||
//check for scroll down
|
recyclerView.post(new Runnable() {
|
||||||
if (dy > 0) {
|
@Override
|
||||||
LinearLayoutManager layoutManager = (LinearLayoutManager) resultRecyclerView.getLayoutManager();
|
public void run() {
|
||||||
visibleItemCount = resultRecyclerView.getLayoutManager().getChildCount();
|
infoListAdapter.showFooter(true);
|
||||||
totalItemCount = resultRecyclerView.getLayoutManager().getItemCount();
|
}
|
||||||
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
});
|
||||||
|
search(searchQuery, pageNumber);
|
||||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading.get()) {
|
|
||||||
pageNumber++;
|
|
||||||
recyclerView.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
infoListAdapter.showFooter(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
search(searchQuery, pageNumber);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void reloadContent() {
|
protected void reloadContent() {
|
||||||
if (DEBUG) Log.d(TAG, "reloadContent() called");
|
if (DEBUG) Log.d(TAG, "reloadContent() called");
|
||||||
|
|
|
@ -155,7 +155,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
|
||||||
//god damen f*** ANDROID SH**
|
//god damn f*** ANDROID SH**
|
||||||
if(holder instanceof InfoItemHolder) {
|
if(holder instanceof InfoItemHolder) {
|
||||||
if(header != null) {
|
if(header != null) {
|
||||||
i--;
|
i--;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.report;
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.acra.collector.CrashReportData;
|
import org.acra.collector.CrashReportData;
|
||||||
import org.acra.sender.ReportSender;
|
import org.acra.sender.ReportSender;
|
||||||
|
@ -30,9 +31,9 @@ import org.schabi.newpipe.R;
|
||||||
public class AcraReportSender implements ReportSender {
|
public class AcraReportSender implements ReportSender {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Context context, CrashReportData report) throws ReportSenderException {
|
public void send(@NonNull Context context, @NonNull CrashReportData report) throws ReportSenderException {
|
||||||
ErrorActivity.reportError(context, report,
|
ErrorActivity.reportError(context, report,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.UI_ERROR,"none",
|
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,"none",
|
||||||
"App crash, UI failure", R.string.app_ui_crash));
|
"App crash, UI failure", R.string.app_ui_crash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.report;
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.acra.config.ACRAConfiguration;
|
import org.acra.config.ACRAConfiguration;
|
||||||
import org.acra.sender.ReportSender;
|
import org.acra.sender.ReportSender;
|
||||||
|
@ -28,7 +29,8 @@ import org.schabi.newpipe.report.AcraReportSender;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class AcraReportSenderFactory implements ReportSenderFactory {
|
public class AcraReportSenderFactory implements ReportSenderFactory {
|
||||||
public ReportSender create(Context context, ACRAConfiguration config) {
|
@NonNull
|
||||||
|
public ReportSender create(@NonNull Context context, @NonNull ACRAConfiguration config) {
|
||||||
return new AcraReportSender();
|
return new AcraReportSender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import android.os.Handler;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.StringRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
|
@ -71,24 +73,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
// BUNDLE TAGS
|
// BUNDLE TAGS
|
||||||
public static final String ERROR_INFO = "error_info";
|
public static final String ERROR_INFO = "error_info";
|
||||||
public static final String ERROR_LIST = "error_list";
|
public static final String ERROR_LIST = "error_list";
|
||||||
// MESSAGE ID
|
|
||||||
public static final int SEARCHED = 0;
|
|
||||||
public static final int REQUESTED_STREAM = 1;
|
|
||||||
public static final int GET_SUGGESTIONS = 2;
|
|
||||||
public static final int SOMETHING_ELSE = 3;
|
|
||||||
public static final int USER_REPORT = 4;
|
|
||||||
public static final int LOAD_IMAGE = 5;
|
|
||||||
public static final int UI_ERROR = 6;
|
|
||||||
public static final int REQUESTED_CHANNEL = 7;
|
|
||||||
// MESSAGE STRING
|
|
||||||
public static final String SEARCHED_STRING = "searched";
|
|
||||||
public static final String REQUESTED_STREAM_STRING = "requested stream";
|
|
||||||
public static final String GET_SUGGESTIONS_STRING = "get suggestions";
|
|
||||||
public static final String SOMETHING_ELSE_STRING = "something";
|
|
||||||
public static final String USER_REPORT_STRING = "user report";
|
|
||||||
public static final String LOAD_IMAGE_STRING = "load image";
|
|
||||||
public static final String UI_ERROR_STRING = "ui error";
|
|
||||||
public static final String REQUESTED_CHANNEL_STRING = "requested channel";
|
|
||||||
public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org";
|
public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org";
|
||||||
public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME;
|
public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME;
|
||||||
Thread globIpRangeThread;
|
Thread globIpRangeThread;
|
||||||
|
@ -105,11 +90,11 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
private TextView errorMessageView;
|
private TextView errorMessageView;
|
||||||
|
|
||||||
public static void reportUiError(final AppCompatActivity activity, final Throwable el) {
|
public static void reportUiError(final AppCompatActivity activity, final Throwable el) {
|
||||||
reportError(activity, el, activity.getClass(), null, ErrorInfo.make(UI_ERROR, "none", "", R.string.app_ui_crash));
|
reportError(activity, el, activity.getClass(), null, ErrorInfo.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reportError(final Context context, final List<Throwable> el,
|
public static void reportError(final Context context, final List<Throwable> el,
|
||||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
final Class returnActivity, View rootView, final ErrorInfo errorInfo) {
|
||||||
|
|
||||||
if (rootView != null) {
|
if (rootView != null) {
|
||||||
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
||||||
|
@ -118,7 +103,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||||
ac.returnActivity = returnAcitivty;
|
ac.returnActivity = returnActivity;
|
||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
@ -128,7 +113,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
}).show();
|
}).show();
|
||||||
} else {
|
} else {
|
||||||
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||||
ac.returnActivity = returnAcitivty;
|
ac.returnActivity = returnActivity;
|
||||||
Intent intent = new Intent(context, ErrorActivity.class);
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||||
|
@ -138,34 +123,34 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reportError(final Context context, final Throwable e,
|
public static void reportError(final Context context, final Throwable e,
|
||||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
final Class returnActivity, View rootView, final ErrorInfo errorInfo) {
|
||||||
List<Throwable> el = null;
|
List<Throwable> el = null;
|
||||||
if(e != null) {
|
if(e != null) {
|
||||||
el = new Vector<>();
|
el = new Vector<>();
|
||||||
el.add(e);
|
el.add(e);
|
||||||
}
|
}
|
||||||
reportError(context, el, returnAcitivty, rootView, errorInfo);
|
reportError(context, el, returnActivity, rootView, errorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// async call
|
// async call
|
||||||
public static void reportError(Handler handler, final Context context, final Throwable e,
|
public static void reportError(Handler handler, final Context context, final Throwable e,
|
||||||
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
final Class returnActivity, final View rootView, final ErrorInfo errorInfo) {
|
||||||
|
|
||||||
List<Throwable> el = null;
|
List<Throwable> el = null;
|
||||||
if(e != null) {
|
if(e != null) {
|
||||||
el = new Vector<>();
|
el = new Vector<>();
|
||||||
el.add(e);
|
el.add(e);
|
||||||
}
|
}
|
||||||
reportError(handler, context, el, returnAcitivty, rootView, errorInfo);
|
reportError(handler, context, el, returnActivity, rootView, errorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// async call
|
// async call
|
||||||
public static void reportError(Handler handler, final Context context, final List<Throwable> el,
|
public static void reportError(Handler handler, final Context context, final List<Throwable> el,
|
||||||
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
final Class returnActivity, final View rootView, final ErrorInfo errorInfo) {
|
||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
reportError(context, el, returnAcitivty, rootView, errorInfo);
|
reportError(context, el, returnActivity, rootView, errorInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -232,7 +217,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
errorInfo = intent.getParcelableExtra(ERROR_INFO);
|
errorInfo = intent.getParcelableExtra(ERROR_INFO);
|
||||||
errorList = intent.getStringArrayExtra(ERROR_LIST);
|
errorList = intent.getStringArrayExtra(ERROR_LIST);
|
||||||
|
|
||||||
//importand add gurumeditaion
|
// important add guru meditation
|
||||||
addGuruMeditaion();
|
addGuruMeditaion();
|
||||||
currentTimeStamp = getCurrentTimeStamp();
|
currentTimeStamp = getCurrentTimeStamp();
|
||||||
|
|
||||||
|
@ -250,7 +235,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
reportButton.setEnabled(false);
|
reportButton.setEnabled(false);
|
||||||
|
|
||||||
globIpRangeThread = new Thread(new IpRagneRequester());
|
globIpRangeThread = new Thread(new IpRangeRequester());
|
||||||
globIpRangeThread.start();
|
globIpRangeThread.start();
|
||||||
|
|
||||||
// normal bugreport
|
// normal bugreport
|
||||||
|
@ -308,17 +293,30 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the checked activity.
|
||||||
|
* @param returnActivity the activity to return to
|
||||||
|
* @return the casted return activity or null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
static Class<? extends Activity> getReturnActivity(Class<?> returnActivity) {
|
||||||
|
Class<? extends Activity> checkedReturnActivity = null;
|
||||||
|
if (returnActivity != null){
|
||||||
|
if (Activity.class.isAssignableFrom(returnActivity)) {
|
||||||
|
checkedReturnActivity = returnActivity.asSubclass(Activity.class);
|
||||||
|
} else {
|
||||||
|
checkedReturnActivity = MainActivity.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkedReturnActivity;
|
||||||
|
}
|
||||||
|
|
||||||
private void goToReturnActivity() {
|
private void goToReturnActivity() {
|
||||||
if (returnActivity == null) {
|
Class<? extends Activity> checkedReturnActivity = getReturnActivity(returnActivity);
|
||||||
|
if (checkedReturnActivity == null) {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
} else {
|
} else {
|
||||||
Intent intent;
|
Intent intent = new Intent(this, checkedReturnActivity);
|
||||||
if (returnActivity != null &&
|
|
||||||
returnActivity.isAssignableFrom(Activity.class)) {
|
|
||||||
intent = new Intent(this, returnActivity);
|
|
||||||
} else {
|
|
||||||
intent = new Intent(this, MainActivity.class);
|
|
||||||
}
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
NavUtils.navigateUpTo(this, intent);
|
NavUtils.navigateUpTo(this, intent);
|
||||||
}
|
}
|
||||||
|
@ -376,26 +374,11 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUserActionString(int userAction) {
|
private String getUserActionString(UserAction userAction) {
|
||||||
switch (userAction) {
|
if(userAction == null) {
|
||||||
case REQUESTED_STREAM:
|
return "Your description is in another castle.";
|
||||||
return REQUESTED_STREAM_STRING;
|
} else {
|
||||||
case SEARCHED:
|
return userAction.getMessage();
|
||||||
return SEARCHED_STRING;
|
|
||||||
case GET_SUGGESTIONS:
|
|
||||||
return GET_SUGGESTIONS_STRING;
|
|
||||||
case SOMETHING_ELSE:
|
|
||||||
return SOMETHING_ELSE_STRING;
|
|
||||||
case USER_REPORT:
|
|
||||||
return USER_REPORT_STRING;
|
|
||||||
case LOAD_IMAGE:
|
|
||||||
return LOAD_IMAGE_STRING;
|
|
||||||
case UI_ERROR:
|
|
||||||
return UI_ERROR_STRING;
|
|
||||||
case REQUESTED_CHANNEL:
|
|
||||||
return REQUESTED_CHANNEL_STRING;
|
|
||||||
default:
|
|
||||||
return "Your description is in another castle.";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,28 +427,28 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
return new ErrorInfo[size];
|
return new ErrorInfo[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
public int userAction;
|
final public UserAction userAction;
|
||||||
public String request;
|
final public String request;
|
||||||
public String serviceName;
|
final public String serviceName;
|
||||||
public int message;
|
@StringRes
|
||||||
|
final public int message;
|
||||||
|
|
||||||
public ErrorInfo() {
|
private ErrorInfo(UserAction userAction, String serviceName, String request, @StringRes int message) {
|
||||||
|
this.userAction = userAction;
|
||||||
|
this.serviceName = serviceName;
|
||||||
|
this.request = request;
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ErrorInfo(Parcel in) {
|
protected ErrorInfo(Parcel in) {
|
||||||
this.userAction = in.readInt();
|
this.userAction = UserAction.valueOf(in.readString());
|
||||||
this.request = in.readString();
|
this.request = in.readString();
|
||||||
this.serviceName = in.readString();
|
this.serviceName = in.readString();
|
||||||
this.message = in.readInt();
|
this.message = in.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ErrorInfo make(int userAction, String serviceName, String request, int message) {
|
public static ErrorInfo make(UserAction userAction, String serviceName, String request, @StringRes int message) {
|
||||||
ErrorInfo info = new ErrorInfo();
|
return new ErrorInfo(userAction, serviceName, request, message);
|
||||||
info.userAction = userAction;
|
|
||||||
info.serviceName = serviceName;
|
|
||||||
info.request = request;
|
|
||||||
info.message = message;
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -475,14 +458,14 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeInt(this.userAction);
|
dest.writeString(this.userAction.name());
|
||||||
dest.writeString(this.request);
|
dest.writeString(this.request);
|
||||||
dest.writeString(this.serviceName);
|
dest.writeString(this.serviceName);
|
||||||
dest.writeInt(this.message);
|
dest.writeInt(this.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IpRagneRequester implements Runnable {
|
private class IpRangeRequester implements Runnable {
|
||||||
Handler h = new Handler();
|
Handler h = new Handler();
|
||||||
public void run() {
|
public void run() {
|
||||||
String ipRange = "none";
|
String ipRange = "none";
|
||||||
|
@ -493,17 +476,16 @@ public class ErrorActivity extends AppCompatActivity {
|
||||||
ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip)
|
ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip)
|
||||||
+ "0.0";
|
+ "0.0";
|
||||||
} catch(Throwable e) {
|
} catch(Throwable e) {
|
||||||
Log.d(TAG, "Error while error: could not get iprange");
|
Log.w(TAG, "Error while error: could not get iprange", e);
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
} finally {
|
||||||
h.post(new IpRageReturnRunnable(ipRange));
|
h.post(new IpRangeReturnRunnable(ipRange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IpRageReturnRunnable implements Runnable {
|
private class IpRangeReturnRunnable implements Runnable {
|
||||||
String ipRange;
|
String ipRange;
|
||||||
public IpRageReturnRunnable(String ipRange) {
|
public IpRangeReturnRunnable(String ipRange) {
|
||||||
this.ipRange = ipRange;
|
this.ipRange = ipRange;
|
||||||
}
|
}
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
26
app/src/main/java/org/schabi/newpipe/report/UserAction.java
Normal file
26
app/src/main/java/org/schabi/newpipe/report/UserAction.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user actions that can cause an error.
|
||||||
|
*/
|
||||||
|
public enum UserAction {
|
||||||
|
SEARCHED("searched"),
|
||||||
|
REQUESTED_STREAM("requested stream"),
|
||||||
|
GET_SUGGESTIONS("get suggestions"),
|
||||||
|
SOMETHING_ELSE("something"),
|
||||||
|
USER_REPORT("user report"),
|
||||||
|
LOAD_IMAGE("load image"),
|
||||||
|
UI_ERROR("ui error"),
|
||||||
|
REQUESTED_CHANNEL("requested channel");
|
||||||
|
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
UserAction(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,9 +72,7 @@ public class NewPipeSettings {
|
||||||
public static String getVideoDownloadPath(Context context) {
|
public static String getVideoDownloadPath(Context context) {
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
final String key = context.getString(R.string.download_path_key);
|
final String key = context.getString(R.string.download_path_key);
|
||||||
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MOVIES);
|
return prefs.getString(key, Environment.DIRECTORY_MOVIES);
|
||||||
|
|
||||||
return downloadPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getAudioDownloadFolder(Context context) {
|
public static File getAudioDownloadFolder(Context context) {
|
||||||
|
@ -84,9 +82,7 @@ public class NewPipeSettings {
|
||||||
public static String getAudioDownloadPath(Context context) {
|
public static String getAudioDownloadPath(Context context) {
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
final String key = context.getString(R.string.download_path_audio_key);
|
final String key = context.getString(R.string.download_path_audio_key);
|
||||||
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MUSIC);
|
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
|
||||||
|
|
||||||
return downloadPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
|
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.report.UserAction.REQUESTED_CHANNEL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract {@link ChannelInfo} with {@link ChannelExtractor} from the given url of the given service
|
* Extract {@link ChannelInfo} with {@link ChannelExtractor} from the given url of the given service
|
||||||
*
|
*
|
||||||
|
@ -67,7 +69,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
ChannelExtractor extractor = getService().getChannelExtractorInstance(url, pageNumber);
|
ChannelExtractor extractor = getService().getChannelExtractorInstance(url, pageNumber);
|
||||||
channelInfo = ChannelInfo.getInfo(extractor);
|
channelInfo = ChannelInfo.getInfo(extractor);
|
||||||
|
|
||||||
if (!channelInfo.errors.isEmpty()) handleErrorsDuringExtraction(channelInfo.errors, ErrorActivity.REQUESTED_CHANNEL);
|
if (!channelInfo.errors.isEmpty()) handleErrorsDuringExtraction(channelInfo.errors, REQUESTED_CHANNEL);
|
||||||
|
|
||||||
if (callback != null && channelInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
if (callback != null && channelInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,7 +95,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
} else if (exception instanceof ParsingException || exception instanceof ExtractionException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_CHANNEL, getServiceName(), url, R.string.parsing_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -101,7 +103,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_CHANNEL, getServiceName(), url, R.string.general_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -59,14 +60,14 @@ public abstract class ExtractorWorker extends AbstractWorker {
|
||||||
* @param errorUserAction what action was the user performing during the error.
|
* @param errorUserAction what action was the user performing during the error.
|
||||||
* (One of the {@link ErrorActivity}.REQUEST_* error (message) ids)
|
* (One of the {@link ErrorActivity}.REQUEST_* error (message) ids)
|
||||||
*/
|
*/
|
||||||
protected void handleErrorsDuringExtraction(List<Throwable> errorsList, int errorUserAction){
|
protected void handleErrorsDuringExtraction(List<Throwable> errorsList, UserAction errorUserAction){
|
||||||
String errorString = "<error id>";
|
String errorString = "<error id>";
|
||||||
switch (errorUserAction) {
|
switch (errorUserAction) {
|
||||||
case ErrorActivity.REQUESTED_STREAM:
|
case REQUESTED_STREAM:
|
||||||
errorString= ErrorActivity.REQUESTED_STREAM_STRING;
|
errorString= errorUserAction.getMessage();
|
||||||
break;
|
break;
|
||||||
case ErrorActivity.REQUESTED_CHANNEL:
|
case REQUESTED_CHANNEL:
|
||||||
errorString= ErrorActivity.REQUESTED_CHANNEL_STRING;
|
errorString= errorUserAction.getMessage();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,13 @@ import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.search.SearchResult;
|
import org.schabi.newpipe.extractor.search.SearchResult;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.report.UserAction.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of results based on a query
|
* Return list of results based on a query
|
||||||
*
|
*
|
||||||
|
@ -106,7 +109,7 @@ public class SearchWorker extends AbstractWorker {
|
||||||
});
|
});
|
||||||
} else if (exception instanceof ExtractionException) {
|
} else if (exception instanceof ExtractionException) {
|
||||||
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(SEARCHED, getServiceName(), query, R.string.parsing_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -115,7 +118,7 @@ public class SearchWorker extends AbstractWorker {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED, getServiceName(), query, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(SEARCHED, getServiceName(), query, R.string.general_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -10,9 +10,12 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.report.UserAction.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
|
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
|
||||||
*
|
*
|
||||||
|
@ -66,7 +69,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
StreamExtractor streamExtractor = getService().getExtractorInstance(url);
|
StreamExtractor streamExtractor = getService().getExtractorInstance(url);
|
||||||
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
||||||
|
|
||||||
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, ErrorActivity.REQUESTED_STREAM);
|
if (streamInfo != null && !streamInfo.errors.isEmpty()) handleErrorsDuringExtraction(streamInfo.errors, REQUESTED_STREAM);
|
||||||
|
|
||||||
if (callback != null && getHandler() != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
if (callback != null && getHandler() != null && streamInfo != null && !isInterrupted()) getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,7 +124,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
});
|
});
|
||||||
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
||||||
// custom service related exceptions
|
// custom service related exceptions
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_STREAM, getServiceName(), url, R.string.youtube_signature_decryption_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -131,9 +134,9 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
||||||
if (!streamInfo.errors.isEmpty()) {
|
if (!streamInfo.errors.isEmpty()) {
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
// !!! if this case ever kicks in someone gets kicked out !!!
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
ErrorActivity.reportError(getHandler(), getContext(), streamInfo.errors, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_STREAM, getServiceName(), url, R.string.could_not_get_stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
|
@ -143,7 +146,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (exception instanceof ParsingException) {
|
} else if (exception instanceof ParsingException) {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_STREAM, getServiceName(), url, R.string.parsing_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -151,7 +154,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_STREAM, getServiceName(), url, R.string.general_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -11,10 +11,13 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.report.UserAction.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Worker that get suggestions based on the query
|
* Worker that get suggestions based on the query
|
||||||
*
|
*
|
||||||
|
@ -79,7 +82,7 @@ public class SuggestionWorker extends AbstractWorker {
|
||||||
|
|
||||||
if (exception instanceof ExtractionException) {
|
if (exception instanceof ExtractionException) {
|
||||||
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.parsing_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(GET_SUGGESTIONS, getServiceName(), query, R.string.parsing_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -95,7 +98,7 @@ public class SuggestionWorker extends AbstractWorker {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
View rootView = getContext() instanceof Activity ? ((Activity) getContext()).findViewById(android.R.id.content) : null;
|
||||||
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.GET_SUGGESTIONS, getServiceName(), query, R.string.general_error));
|
ErrorActivity.reportError(getHandler(), getContext(), exception, null, rootView, ErrorActivity.ErrorInfo.make(GET_SUGGESTIONS, getServiceName(), query, R.string.general_error));
|
||||||
getHandler().post(new Runnable() {
|
getHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -9,12 +9,14 @@ public interface DownloadDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all missions
|
* Load all missions
|
||||||
|
*
|
||||||
* @return a list of download missions
|
* @return a list of download missions
|
||||||
*/
|
*/
|
||||||
List<DownloadMission> loadMissions();
|
List<DownloadMission> loadMissions();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a downlaod mission to the storage
|
* Add a download mission to the storage
|
||||||
|
*
|
||||||
* @param downloadMission the download mission to add
|
* @param downloadMission the download mission to add
|
||||||
* @return the identifier of the mission
|
* @return the identifier of the mission
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +24,7 @@ public interface DownloadDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a download mission which exists in the storage
|
* Update a download mission which exists in the storage
|
||||||
|
*
|
||||||
* @param downloadMission the download mission to update
|
* @param downloadMission the download mission to update
|
||||||
* @throws IllegalArgumentException if the mission was not added to storage
|
* @throws IllegalArgumentException if the mission was not added to storage
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +33,7 @@ public interface DownloadDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a download mission
|
* Delete a download mission
|
||||||
|
*
|
||||||
* @param downloadMission the mission to delete
|
* @param downloadMission the mission to delete
|
||||||
*/
|
*/
|
||||||
void deleteMission(DownloadMission downloadMission);
|
void deleteMission(DownloadMission downloadMission);
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
package us.shandian.giga.get;
|
package us.shandian.giga.get;
|
||||||
|
|
||||||
public interface DownloadManager
|
public interface DownloadManager {
|
||||||
{
|
int BLOCK_SIZE = 512 * 1024;
|
||||||
int BLOCK_SIZE = 512 * 1024;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a new download mission
|
* Start a new download mission
|
||||||
* @param url the url to download
|
*
|
||||||
* @param location the location
|
* @param url the url to download
|
||||||
* @param name the name of the file to create
|
* @param location the location
|
||||||
* @param isAudio true if the download is an audio file
|
* @param name the name of the file to create
|
||||||
* @param threads the number of threads maximal used to download chunks of the file. @return the identifier of the mission.
|
* @param isAudio true if the download is an audio file
|
||||||
|
* @param threads the number of threads maximal used to download chunks of the file. @return the identifier of the mission.
|
||||||
*/
|
*/
|
||||||
int startMission(String url, String location, String name, boolean isAudio, int threads);
|
int startMission(String url, String location, String name, boolean isAudio, int threads);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resume the execution of a download mission.
|
* Resume the execution of a download mission.
|
||||||
* @param id the identifier of the mission to resume.
|
*
|
||||||
*/
|
* @param id the identifier of the mission to resume.
|
||||||
void resumeMission(int id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause the execution of a download mission.
|
|
||||||
* @param id the identifier of the mission to pause.
|
|
||||||
*/
|
*/
|
||||||
void pauseMission(int id);
|
void resumeMission(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the mission from the downloaded list but keeps the downloaded file.
|
* Pause the execution of a download mission.
|
||||||
* @param id The mission identifier
|
*
|
||||||
|
* @param id the identifier of the mission to pause.
|
||||||
*/
|
*/
|
||||||
void deleteMission(int id);
|
void pauseMission(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the download mission by its identifier
|
* Deletes the mission from the downloaded list but keeps the downloaded file.
|
||||||
* @param id the identifier of the download mission
|
*
|
||||||
* @return the download mission or null if the mission doesn't exist
|
* @param id The mission identifier
|
||||||
*/
|
*/
|
||||||
DownloadMission getMission(int id);
|
void deleteMission(int id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of download missions.
|
* Get the download mission by its identifier
|
||||||
* @return the number of download missions.
|
*
|
||||||
|
* @param id the identifier of the download mission
|
||||||
|
* @return the download mission or null if the mission doesn't exist
|
||||||
*/
|
*/
|
||||||
int getCount();
|
DownloadMission getMission(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of download missions.
|
||||||
|
*
|
||||||
|
* @return the number of download missions.
|
||||||
|
*/
|
||||||
|
int getCount();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,95 +18,96 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import us.shandian.giga.util.Utility;
|
import us.shandian.giga.util.Utility;
|
||||||
|
|
||||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||||
|
|
||||||
public class DownloadManagerImpl implements DownloadManager
|
public class DownloadManagerImpl implements DownloadManager {
|
||||||
{
|
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
|
||||||
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
|
private final DownloadDataSource mDownloadDataSource;
|
||||||
private final DownloadDataSource mDownloadDataSource;
|
|
||||||
|
|
||||||
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance
|
* Create a new instance
|
||||||
* @param searchLocations the directories to search for unfinished downloads
|
*
|
||||||
|
* @param searchLocations the directories to search for unfinished downloads
|
||||||
* @param downloadDataSource the data source for finished downloads
|
* @param downloadDataSource the data source for finished downloads
|
||||||
*/
|
*/
|
||||||
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
|
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
|
||||||
mDownloadDataSource = downloadDataSource;
|
mDownloadDataSource = downloadDataSource;
|
||||||
loadMissions(searchLocations);
|
loadMissions(searchLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
|
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
|
||||||
DownloadMission existingMission = getMissionByLocation(location, name);
|
DownloadMission existingMission = getMissionByLocation(location, name);
|
||||||
if(existingMission != null) {
|
if (existingMission != null) {
|
||||||
// Already downloaded or downloading
|
// Already downloaded or downloading
|
||||||
if(existingMission.finished) {
|
if (existingMission.finished) {
|
||||||
// Overwrite mission
|
// Overwrite mission
|
||||||
deleteMission(mMissions.indexOf(existingMission));
|
deleteMission(mMissions.indexOf(existingMission));
|
||||||
} else {
|
} else {
|
||||||
// Rename file (?)
|
// Rename file (?)
|
||||||
try {
|
try {
|
||||||
name = generateUniqueName(location, name);
|
name = generateUniqueName(location, name);
|
||||||
}catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Unable to generate unique name", e);
|
Log.e(TAG, "Unable to generate unique name", e);
|
||||||
name = System.currentTimeMillis() + name ;
|
name = System.currentTimeMillis() + name;
|
||||||
Log.i(TAG, "Using " + name);
|
Log.i(TAG, "Using " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadMission mission = new DownloadMission(name, url, location);
|
DownloadMission mission = new DownloadMission(name, url, location);
|
||||||
mission.timestamp = System.currentTimeMillis();
|
mission.timestamp = System.currentTimeMillis();
|
||||||
mission.threadCount = threads;
|
mission.threadCount = threads;
|
||||||
mission.addListener(new MissionListener(mission));
|
mission.addListener(new MissionListener(mission));
|
||||||
new Initializer(mission).start();
|
new Initializer(mission).start();
|
||||||
return insertMission(mission);
|
return insertMission(mission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resumeMission(int i) {
|
public void resumeMission(int i) {
|
||||||
DownloadMission d = getMission(i);
|
DownloadMission d = getMission(i);
|
||||||
if (!d.running && d.errCode == -1) {
|
if (!d.running && d.errCode == -1) {
|
||||||
d.start();
|
d.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pauseMission(int i) {
|
public void pauseMission(int i) {
|
||||||
DownloadMission d = getMission(i);
|
DownloadMission d = getMission(i);
|
||||||
if (d.running) {
|
if (d.running) {
|
||||||
d.pause();
|
d.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteMission(int i) {
|
public void deleteMission(int i) {
|
||||||
DownloadMission mission = getMission(i);
|
DownloadMission mission = getMission(i);
|
||||||
if(mission.finished) {
|
if (mission.finished) {
|
||||||
mDownloadDataSource.deleteMission(mission);
|
mDownloadDataSource.deleteMission(mission);
|
||||||
}
|
}
|
||||||
mission.delete();
|
mission.delete();
|
||||||
mMissions.remove(i);
|
mMissions.remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMissions(Iterable<String> searchLocations) {
|
private void loadMissions(Iterable<String> searchLocations) {
|
||||||
mMissions.clear();
|
mMissions.clear();
|
||||||
loadFinishedMissions();
|
loadFinishedMissions();
|
||||||
for(String location: searchLocations) {
|
for (String location : searchLocations) {
|
||||||
loadMissions(location);
|
loadMissions(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads finished missions from the data source
|
* Loads finished missions from the data source
|
||||||
*/
|
*/
|
||||||
private void loadFinishedMissions() {
|
private void loadFinishedMissions() {
|
||||||
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
|
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
|
||||||
if(finishedMissions == null) {
|
if (finishedMissions == null) {
|
||||||
finishedMissions = new ArrayList<>();
|
finishedMissions = new ArrayList<>();
|
||||||
}
|
}
|
||||||
// Ensure its sorted
|
// Ensure its sorted
|
||||||
|
@ -117,251 +118,255 @@ public class DownloadManagerImpl implements DownloadManager
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
|
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
|
||||||
for(DownloadMission mission: finishedMissions) {
|
for (DownloadMission mission : finishedMissions) {
|
||||||
File downloadedFile = mission.getDownloadedFile();
|
File downloadedFile = mission.getDownloadedFile();
|
||||||
if(!downloadedFile.isFile()) {
|
if (!downloadedFile.isFile()) {
|
||||||
if(DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
|
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
|
||||||
}
|
}
|
||||||
mDownloadDataSource.deleteMission(mission);
|
mDownloadDataSource.deleteMission(mission);
|
||||||
} else {
|
} else {
|
||||||
mission.length = downloadedFile.length();
|
mission.length = downloadedFile.length();
|
||||||
mission.finished = true;
|
mission.finished = true;
|
||||||
mission.running = false;
|
mission.running = false;
|
||||||
mMissions.add(mission);
|
mMissions.add(mission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMissions(String location) {
|
private void loadMissions(String location) {
|
||||||
|
|
||||||
File f = new File(location);
|
File f = new File(location);
|
||||||
|
|
||||||
if (f.exists() && f.isDirectory()) {
|
if (f.exists() && f.isDirectory()) {
|
||||||
File[] subs = f.listFiles();
|
File[] subs = f.listFiles();
|
||||||
|
|
||||||
if(subs == null) {
|
if (subs == null) {
|
||||||
Log.e(TAG, "listFiles() returned null");
|
Log.e(TAG, "listFiles() returned null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File sub : subs) {
|
for (File sub : subs) {
|
||||||
if (sub.isFile() && sub.getName().endsWith(".giga")) {
|
if (sub.isFile() && sub.getName().endsWith(".giga")) {
|
||||||
String str = Utility.readFromFile(sub.getAbsolutePath());
|
String str = Utility.readFromFile(sub.getAbsolutePath());
|
||||||
if (str != null && !str.trim().equals("")) {
|
if (str != null && !str.trim().equals("")) {
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "loading mission " + sub.getName());
|
Log.d(TAG, "loading mission " + sub.getName());
|
||||||
Log.d(TAG, str);
|
Log.d(TAG, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
|
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
|
||||||
|
|
||||||
if (mis.finished) {
|
if (mis.finished) {
|
||||||
if(!sub.delete()) {
|
if (!sub.delete()) {
|
||||||
Log.w(TAG, "Unable to delete .giga file: " + sub.getPath());
|
Log.w(TAG, "Unable to delete .giga file: " + sub.getPath());
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mis.running = false;
|
mis.running = false;
|
||||||
mis.recovered = true;
|
mis.recovered = true;
|
||||||
insertMission(mis);
|
insertMission(mis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DownloadMission getMission(int i) {
|
public DownloadMission getMission(int i) {
|
||||||
return mMissions.get(i);
|
return mMissions.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return mMissions.size();
|
return mMissions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int insertMission(DownloadMission mission) {
|
private int insertMission(DownloadMission mission) {
|
||||||
int i = -1;
|
int i = -1;
|
||||||
|
|
||||||
DownloadMission m = null;
|
DownloadMission m = null;
|
||||||
|
|
||||||
if (mMissions.size() > 0) {
|
if (mMissions.size() > 0) {
|
||||||
do {
|
do {
|
||||||
m = mMissions.get(++i);
|
m = mMissions.get(++i);
|
||||||
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
|
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
|
||||||
|
|
||||||
//if (i > 0) i--;
|
//if (i > 0) i--;
|
||||||
} else {
|
} else {
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMissions.add(i, mission);
|
mMissions.add(i, mission);
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a mission by its location and name
|
* Get a mission by its location and name
|
||||||
* @param location the location
|
*
|
||||||
* @param name the name
|
* @param location the location
|
||||||
* @return the mission or null if no such mission exists
|
* @param name the name
|
||||||
*/
|
* @return the mission or null if no such mission exists
|
||||||
private @Nullable DownloadMission getMissionByLocation(String location, String name) {
|
|
||||||
for(DownloadMission mission: mMissions) {
|
|
||||||
if(location.equals(mission.location) && name.equals(mission.name)) {
|
|
||||||
return mission;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits the filename into name and extension
|
|
||||||
*
|
|
||||||
* Dots are ignored if they appear: not at all, at the beginning of the file,
|
|
||||||
* at the end of the file
|
|
||||||
*
|
|
||||||
* @param name the name to split
|
|
||||||
* @return a string array with a length of 2 containing the name and the extension
|
|
||||||
*/
|
*/
|
||||||
private static String[] splitName(String name) {
|
private
|
||||||
int dotIndex = name.lastIndexOf('.');
|
@Nullable
|
||||||
if(dotIndex <= 0 || (dotIndex == name.length() - 1)) {
|
DownloadMission getMissionByLocation(String location, String name) {
|
||||||
return new String[]{name, ""};
|
for (DownloadMission mission : mMissions) {
|
||||||
} else {
|
if (location.equals(mission.location) && name.equals(mission.name)) {
|
||||||
return new String[]{name.substring(0, dotIndex), name.substring(dotIndex + 1)};
|
return mission;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a unique file name.
|
* Splits the filename into name and extension
|
||||||
*
|
* <p>
|
||||||
* e.g. "myname (1).txt" if the name "myname.txt" exists.
|
* Dots are ignored if they appear: not at all, at the beginning of the file,
|
||||||
* @param location the location (to check for existing files)
|
* at the end of the file
|
||||||
* @param name the name of the file
|
*
|
||||||
|
* @param name the name to split
|
||||||
|
* @return a string array with a length of 2 containing the name and the extension
|
||||||
|
*/
|
||||||
|
private static String[] splitName(String name) {
|
||||||
|
int dotIndex = name.lastIndexOf('.');
|
||||||
|
if (dotIndex <= 0 || (dotIndex == name.length() - 1)) {
|
||||||
|
return new String[]{name, ""};
|
||||||
|
} else {
|
||||||
|
return new String[]{name.substring(0, dotIndex), name.substring(dotIndex + 1)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a unique file name.
|
||||||
|
* <p>
|
||||||
|
* e.g. "myname (1).txt" if the name "myname.txt" exists.
|
||||||
|
*
|
||||||
|
* @param location the location (to check for existing files)
|
||||||
|
* @param name the name of the file
|
||||||
* @return the unique file name
|
* @return the unique file name
|
||||||
* @throws IllegalArgumentException if the location is not a directory
|
* @throws IllegalArgumentException if the location is not a directory
|
||||||
* @throws SecurityException if the location is not readable
|
* @throws SecurityException if the location is not readable
|
||||||
*/
|
*/
|
||||||
private static String generateUniqueName(String location, String name) {
|
private static String generateUniqueName(String location, String name) {
|
||||||
if(location == null) throw new NullPointerException("location is null");
|
if (location == null) throw new NullPointerException("location is null");
|
||||||
if(name == null) throw new NullPointerException("name is null");
|
if (name == null) throw new NullPointerException("name is null");
|
||||||
File destination = new File(location);
|
File destination = new File(location);
|
||||||
if(!destination.isDirectory()) {
|
if (!destination.isDirectory()) {
|
||||||
throw new IllegalArgumentException("location is not a directory: " + location);
|
throw new IllegalArgumentException("location is not a directory: " + location);
|
||||||
}
|
}
|
||||||
final String[] nameParts = splitName(name);
|
final String[] nameParts = splitName(name);
|
||||||
String[] existingName = destination.list(new FilenameFilter() {
|
String[] existingName = destination.list(new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File dir, String name) {
|
public boolean accept(File dir, String name) {
|
||||||
return name.startsWith(nameParts[0]);
|
return name.startsWith(nameParts[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Arrays.sort(existingName);
|
Arrays.sort(existingName);
|
||||||
String newName;
|
String newName;
|
||||||
int downloadIndex = 0;
|
int downloadIndex = 0;
|
||||||
do {
|
do {
|
||||||
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
|
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
|
||||||
++downloadIndex;
|
++downloadIndex;
|
||||||
if(downloadIndex == 1000) { // Probably an error on our side
|
if (downloadIndex == 1000) { // Probably an error on our side
|
||||||
throw new RuntimeException("Too many existing files");
|
throw new RuntimeException("Too many existing files");
|
||||||
}
|
}
|
||||||
} while (Arrays.binarySearch(existingName, newName) >= 0);
|
} while (Arrays.binarySearch(existingName, newName) >= 0);
|
||||||
return newName;
|
return newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Initializer extends Thread {
|
private class Initializer extends Thread {
|
||||||
private DownloadMission mission;
|
private DownloadMission mission;
|
||||||
|
|
||||||
public Initializer(DownloadMission mission) {
|
|
||||||
this.mission = mission;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
URL url = new URL(mission.url);
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
mission.length = conn.getContentLength();
|
|
||||||
|
|
||||||
if (mission.length <= 0) {
|
|
||||||
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
|
||||||
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open again
|
|
||||||
conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
|
|
||||||
|
|
||||||
if (conn.getResponseCode() != 206) {
|
|
||||||
// Fallback to single thread if no partial content support
|
|
||||||
mission.fallback = true;
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "falling back");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "response = " + conn.getResponseCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
mission.blocks = mission.length / BLOCK_SIZE;
|
|
||||||
|
|
||||||
if (mission.threadCount > mission.blocks) {
|
|
||||||
mission.threadCount = (int) mission.blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mission.threadCount <= 0) {
|
|
||||||
mission.threadCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mission.blocks * BLOCK_SIZE < mission.length) {
|
|
||||||
mission.blocks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
new File(mission.location).mkdirs();
|
public Initializer(DownloadMission mission) {
|
||||||
new File(mission.location + "/" + mission.name).createNewFile();
|
this.mission = mission;
|
||||||
RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw");
|
}
|
||||||
af.setLength(mission.length);
|
|
||||||
af.close();
|
|
||||||
|
|
||||||
mission.start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Notify
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Waits for mission to finish to add it to the {@link #mDownloadDataSource}
|
public void run() {
|
||||||
*/
|
try {
|
||||||
private class MissionListener implements DownloadMission.MissionListener {
|
URL url = new URL(mission.url);
|
||||||
private final DownloadMission mMission;
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
mission.length = conn.getContentLength();
|
||||||
|
|
||||||
private MissionListener(DownloadMission mission) {
|
if (mission.length <= 0) {
|
||||||
if(mission == null) throw new NullPointerException("mission is null");
|
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||||
// Could the mission be passed in onFinish()?
|
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||||
mMission = mission;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Open again
|
||||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
}
|
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
|
||||||
|
|
||||||
@Override
|
if (conn.getResponseCode() != 206) {
|
||||||
public void onFinish(DownloadMission downloadMission) {
|
// Fallback to single thread if no partial content support
|
||||||
mDownloadDataSource.addMission(mMission);
|
mission.fallback = true;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (DEBUG) {
|
||||||
public void onError(DownloadMission downloadMission, int errCode) {
|
Log.d(TAG, "falling back");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "response = " + conn.getResponseCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
mission.blocks = mission.length / BLOCK_SIZE;
|
||||||
|
|
||||||
|
if (mission.threadCount > mission.blocks) {
|
||||||
|
mission.threadCount = (int) mission.blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mission.threadCount <= 0) {
|
||||||
|
mission.threadCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mission.blocks * BLOCK_SIZE < mission.length) {
|
||||||
|
mission.blocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
new File(mission.location).mkdirs();
|
||||||
|
new File(mission.location + "/" + mission.name).createNewFile();
|
||||||
|
RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw");
|
||||||
|
af.setLength(mission.length);
|
||||||
|
af.close();
|
||||||
|
|
||||||
|
mission.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO Notify
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for mission to finish to add it to the {@link #mDownloadDataSource}
|
||||||
|
*/
|
||||||
|
private class MissionListener implements DownloadMission.MissionListener {
|
||||||
|
private final DownloadMission mMission;
|
||||||
|
|
||||||
|
private MissionListener(DownloadMission mission) {
|
||||||
|
if (mission == null) throw new NullPointerException("mission is null");
|
||||||
|
// Could the mission be passed in onFinish()?
|
||||||
|
mMission = mission;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinish(DownloadMission downloadMission) {
|
||||||
|
mDownloadDataSource.addMission(mMission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(DownloadMission downloadMission, int errCode) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,311 +18,315 @@ import us.shandian.giga.util.Utility;
|
||||||
|
|
||||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||||
|
|
||||||
public class DownloadMission
|
public class DownloadMission {
|
||||||
{
|
private static final String TAG = DownloadMission.class.getSimpleName();
|
||||||
private static final String TAG = DownloadMission.class.getSimpleName();
|
|
||||||
|
|
||||||
public interface MissionListener {
|
public interface MissionListener {
|
||||||
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
|
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
|
||||||
|
|
||||||
void onProgressUpdate(DownloadMission downloadMission, long done, long total);
|
|
||||||
void onFinish(DownloadMission downloadMission);
|
|
||||||
void onError(DownloadMission downloadMission, int errCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final int ERROR_SERVER_UNSUPPORTED = 206;
|
|
||||||
public static final int ERROR_UNKNOWN = 233;
|
|
||||||
|
|
||||||
/**
|
void onProgressUpdate(DownloadMission downloadMission, long done, long total);
|
||||||
* The filename
|
|
||||||
*/
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/**
|
void onFinish(DownloadMission downloadMission);
|
||||||
* The url of the file to download
|
|
||||||
*/
|
|
||||||
public String url;
|
|
||||||
|
|
||||||
/**
|
void onError(DownloadMission downloadMission, int errCode);
|
||||||
* The directory to store the download
|
}
|
||||||
*/
|
|
||||||
public String location;
|
|
||||||
|
|
||||||
/**
|
public static final int ERROR_SERVER_UNSUPPORTED = 206;
|
||||||
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
|
public static final int ERROR_UNKNOWN = 233;
|
||||||
*/
|
|
||||||
public long blocks;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of bytes
|
* The filename
|
||||||
*/
|
|
||||||
public long length;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bytes downloaded
|
|
||||||
*/
|
|
||||||
public long done;
|
|
||||||
public int threadCount = 3;
|
|
||||||
public int finishCount;
|
|
||||||
private List<Long> threadPositions = new ArrayList<Long>();
|
|
||||||
public final Map<Long, Boolean> blockState = new HashMap<Long, Boolean>();
|
|
||||||
public boolean running;
|
|
||||||
public boolean finished;
|
|
||||||
public boolean fallback;
|
|
||||||
public int errCode = -1;
|
|
||||||
public long timestamp;
|
|
||||||
|
|
||||||
public transient boolean recovered;
|
|
||||||
|
|
||||||
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>();
|
|
||||||
private transient boolean mWritingToFile;
|
|
||||||
|
|
||||||
private static final int NO_IDENTIFIER = -1;
|
|
||||||
private long db_identifier = NO_IDENTIFIER;
|
|
||||||
|
|
||||||
public DownloadMission() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadMission(String name, String url, String location) {
|
|
||||||
if(name == null) throw new NullPointerException("name is null");
|
|
||||||
if(name.isEmpty()) throw new IllegalArgumentException("name is empty");
|
|
||||||
if(url == null) throw new NullPointerException("url is null");
|
|
||||||
if(url.isEmpty()) throw new IllegalArgumentException("url is empty");
|
|
||||||
if(location == null) throw new NullPointerException("location is null");
|
|
||||||
if(location.isEmpty()) throw new IllegalArgumentException("location is empty");
|
|
||||||
this.url = url;
|
|
||||||
this.name = name;
|
|
||||||
this.location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void checkBlock(long block) {
|
|
||||||
if(block < 0 || block >= blocks) {
|
|
||||||
throw new IllegalArgumentException("illegal block identifier");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a block is reserved
|
|
||||||
* @param block the block identifier
|
|
||||||
* @return true if the block is reserved and false if otherwise
|
|
||||||
*/
|
*/
|
||||||
public boolean isBlockPreserved(long block) {
|
public String name;
|
||||||
checkBlock(block);
|
|
||||||
return blockState.containsKey(block) ? blockState.get(block) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void preserveBlock(long block) {
|
|
||||||
checkBlock(block);
|
|
||||||
synchronized (blockState) {
|
|
||||||
blockState.put(block, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the download position of the file
|
* The url of the file to download
|
||||||
* @param threadId the identifier of the thread
|
|
||||||
* @param position the download position of the thread
|
|
||||||
*/
|
*/
|
||||||
public void setPosition(int threadId, long position) {
|
public String url;
|
||||||
threadPositions.set(threadId, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the position of a thread
|
* The directory to store the download
|
||||||
* @param threadId the identifier of the thread
|
|
||||||
* @return the position for the thread
|
|
||||||
*/
|
*/
|
||||||
public long getPosition(int threadId) {
|
public String location;
|
||||||
return threadPositions.get(threadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void notifyProgress(long deltaLen) {
|
|
||||||
if (!running) return;
|
|
||||||
|
|
||||||
if (recovered) {
|
|
||||||
recovered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
done += deltaLen;
|
|
||||||
|
|
||||||
if (done > length) {
|
|
||||||
done = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done != length) {
|
|
||||||
writeThisToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (WeakReference<MissionListener> ref: mListeners) {
|
|
||||||
final MissionListener listener = ref.get();
|
|
||||||
if (listener != null) {
|
|
||||||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
listener.onProgressUpdate(DownloadMission.this, done, length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by a download thread when it finished.
|
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
|
||||||
*/
|
|
||||||
public synchronized void notifyFinished() {
|
|
||||||
if (errCode > 0) return;
|
|
||||||
|
|
||||||
finishCount++;
|
|
||||||
|
|
||||||
if (finishCount == threadCount) {
|
|
||||||
onFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when all parts are downloaded
|
|
||||||
*/
|
|
||||||
private void onFinish() {
|
|
||||||
if (errCode > 0) return;
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "onFinish");
|
|
||||||
}
|
|
||||||
|
|
||||||
running = false;
|
|
||||||
finished = true;
|
|
||||||
|
|
||||||
deleteThisFromFile();
|
|
||||||
|
|
||||||
for (WeakReference<MissionListener> ref : mListeners) {
|
|
||||||
final MissionListener listener = ref.get();
|
|
||||||
if (listener != null) {
|
|
||||||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
listener.onFinish(DownloadMission.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void notifyError(int err) {
|
|
||||||
errCode = err;
|
|
||||||
|
|
||||||
writeThisToFile();
|
|
||||||
|
|
||||||
for (WeakReference<MissionListener> ref : mListeners) {
|
|
||||||
final MissionListener listener = ref.get();
|
|
||||||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
listener.onError(DownloadMission.this, errCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void addListener(MissionListener listener) {
|
|
||||||
Handler handler = new Handler(Looper.getMainLooper());
|
|
||||||
MissionListener.handlerStore.put(listener, handler);
|
|
||||||
mListeners.add(new WeakReference<MissionListener>(listener));
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void removeListener(MissionListener listener) {
|
|
||||||
for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator();
|
|
||||||
iterator.hasNext(); ) {
|
|
||||||
WeakReference<MissionListener> weakRef = iterator.next();
|
|
||||||
if (listener!=null && listener == weakRef.get())
|
|
||||||
{
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start downloading with multiple threads.
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
if (!running && !finished) {
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
if (!fallback) {
|
|
||||||
for (int i = 0; i < threadCount; i++) {
|
|
||||||
if (threadPositions.size() <= i && !recovered) {
|
|
||||||
threadPositions.add((long) i);
|
|
||||||
}
|
|
||||||
new Thread(new DownloadRunnable(this, i)).start();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// In fallback mode, resuming is not supported.
|
|
||||||
threadCount = 1;
|
|
||||||
done = 0;
|
|
||||||
blocks = 0;
|
|
||||||
new Thread(new DownloadRunnableFallback(this)).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pause() {
|
|
||||||
if (running) {
|
|
||||||
running = false;
|
|
||||||
recovered = true;
|
|
||||||
|
|
||||||
// TODO: Notify & Write state to info file
|
|
||||||
// if (err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the file and the meta file
|
|
||||||
*/
|
|
||||||
public void delete() {
|
|
||||||
deleteThisFromFile();
|
|
||||||
new File(location, name).delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write this {@link DownloadMission} to the meta file asynchronously
|
|
||||||
* if no thread is already running.
|
|
||||||
*/
|
|
||||||
public void writeThisToFile() {
|
|
||||||
if (!mWritingToFile) {
|
|
||||||
mWritingToFile = true;
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
doWriteThisToFile();
|
|
||||||
mWritingToFile = false;
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write this {@link DownloadMission} to the meta file.
|
|
||||||
*/
|
|
||||||
private void doWriteThisToFile() {
|
|
||||||
synchronized (blockState) {
|
|
||||||
Utility.writeToFile(getMetaFilename(), new Gson().toJson(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteThisFromFile() {
|
|
||||||
new File(getMetaFilename()).delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path of the meta file
|
|
||||||
* @return the path to the meta file
|
|
||||||
*/
|
*/
|
||||||
private String getMetaFilename() {
|
public long blocks;
|
||||||
return location + "/" + name + ".giga";
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getDownloadedFile() {
|
/**
|
||||||
return new File(location, name);
|
* Number of bytes
|
||||||
}
|
*/
|
||||||
|
public long length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of bytes downloaded
|
||||||
|
*/
|
||||||
|
public long done;
|
||||||
|
public int threadCount = 3;
|
||||||
|
public int finishCount;
|
||||||
|
private List<Long> threadPositions = new ArrayList<Long>();
|
||||||
|
public final Map<Long, Boolean> blockState = new HashMap<Long, Boolean>();
|
||||||
|
public boolean running;
|
||||||
|
public boolean finished;
|
||||||
|
public boolean fallback;
|
||||||
|
public int errCode = -1;
|
||||||
|
public long timestamp;
|
||||||
|
|
||||||
|
public transient boolean recovered;
|
||||||
|
|
||||||
|
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>();
|
||||||
|
private transient boolean mWritingToFile;
|
||||||
|
|
||||||
|
private static final int NO_IDENTIFIER = -1;
|
||||||
|
private long db_identifier = NO_IDENTIFIER;
|
||||||
|
|
||||||
|
public DownloadMission() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadMission(String name, String url, String location) {
|
||||||
|
if (name == null) throw new NullPointerException("name is null");
|
||||||
|
if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
|
||||||
|
if (url == null) throw new NullPointerException("url is null");
|
||||||
|
if (url.isEmpty()) throw new IllegalArgumentException("url is empty");
|
||||||
|
if (location == null) throw new NullPointerException("location is null");
|
||||||
|
if (location.isEmpty()) throw new IllegalArgumentException("location is empty");
|
||||||
|
this.url = url;
|
||||||
|
this.name = name;
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void checkBlock(long block) {
|
||||||
|
if (block < 0 || block >= blocks) {
|
||||||
|
throw new IllegalArgumentException("illegal block identifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a block is reserved
|
||||||
|
*
|
||||||
|
* @param block the block identifier
|
||||||
|
* @return true if the block is reserved and false if otherwise
|
||||||
|
*/
|
||||||
|
public boolean isBlockPreserved(long block) {
|
||||||
|
checkBlock(block);
|
||||||
|
return blockState.containsKey(block) ? blockState.get(block) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preserveBlock(long block) {
|
||||||
|
checkBlock(block);
|
||||||
|
synchronized (blockState) {
|
||||||
|
blockState.put(block, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the download position of the file
|
||||||
|
*
|
||||||
|
* @param threadId the identifier of the thread
|
||||||
|
* @param position the download position of the thread
|
||||||
|
*/
|
||||||
|
public void setPosition(int threadId, long position) {
|
||||||
|
threadPositions.set(threadId, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position of a thread
|
||||||
|
*
|
||||||
|
* @param threadId the identifier of the thread
|
||||||
|
* @return the position for the thread
|
||||||
|
*/
|
||||||
|
public long getPosition(int threadId) {
|
||||||
|
return threadPositions.get(threadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void notifyProgress(long deltaLen) {
|
||||||
|
if (!running) return;
|
||||||
|
|
||||||
|
if (recovered) {
|
||||||
|
recovered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
done += deltaLen;
|
||||||
|
|
||||||
|
if (done > length) {
|
||||||
|
done = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done != length) {
|
||||||
|
writeThisToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (WeakReference<MissionListener> ref : mListeners) {
|
||||||
|
final MissionListener listener = ref.get();
|
||||||
|
if (listener != null) {
|
||||||
|
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onProgressUpdate(DownloadMission.this, done, length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a download thread when it finished.
|
||||||
|
*/
|
||||||
|
public synchronized void notifyFinished() {
|
||||||
|
if (errCode > 0) return;
|
||||||
|
|
||||||
|
finishCount++;
|
||||||
|
|
||||||
|
if (finishCount == threadCount) {
|
||||||
|
onFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when all parts are downloaded
|
||||||
|
*/
|
||||||
|
private void onFinish() {
|
||||||
|
if (errCode > 0) return;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onFinish");
|
||||||
|
}
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
finished = true;
|
||||||
|
|
||||||
|
deleteThisFromFile();
|
||||||
|
|
||||||
|
for (WeakReference<MissionListener> ref : mListeners) {
|
||||||
|
final MissionListener listener = ref.get();
|
||||||
|
if (listener != null) {
|
||||||
|
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onFinish(DownloadMission.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void notifyError(int err) {
|
||||||
|
errCode = err;
|
||||||
|
|
||||||
|
writeThisToFile();
|
||||||
|
|
||||||
|
for (WeakReference<MissionListener> ref : mListeners) {
|
||||||
|
final MissionListener listener = ref.get();
|
||||||
|
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onError(DownloadMission.this, errCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addListener(MissionListener listener) {
|
||||||
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
MissionListener.handlerStore.put(listener, handler);
|
||||||
|
mListeners.add(new WeakReference<MissionListener>(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeListener(MissionListener listener) {
|
||||||
|
for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator();
|
||||||
|
iterator.hasNext(); ) {
|
||||||
|
WeakReference<MissionListener> weakRef = iterator.next();
|
||||||
|
if (listener != null && listener == weakRef.get()) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start downloading with multiple threads.
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
if (!running && !finished) {
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
if (!fallback) {
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
if (threadPositions.size() <= i && !recovered) {
|
||||||
|
threadPositions.add((long) i);
|
||||||
|
}
|
||||||
|
new Thread(new DownloadRunnable(this, i)).start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In fallback mode, resuming is not supported.
|
||||||
|
threadCount = 1;
|
||||||
|
done = 0;
|
||||||
|
blocks = 0;
|
||||||
|
new Thread(new DownloadRunnableFallback(this)).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
if (running) {
|
||||||
|
running = false;
|
||||||
|
recovered = true;
|
||||||
|
|
||||||
|
// TODO: Notify & Write state to info file
|
||||||
|
// if (err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the file and the meta file
|
||||||
|
*/
|
||||||
|
public void delete() {
|
||||||
|
deleteThisFromFile();
|
||||||
|
new File(location, name).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this {@link DownloadMission} to the meta file asynchronously
|
||||||
|
* if no thread is already running.
|
||||||
|
*/
|
||||||
|
public void writeThisToFile() {
|
||||||
|
if (!mWritingToFile) {
|
||||||
|
mWritingToFile = true;
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doWriteThisToFile();
|
||||||
|
mWritingToFile = false;
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this {@link DownloadMission} to the meta file.
|
||||||
|
*/
|
||||||
|
private void doWriteThisToFile() {
|
||||||
|
synchronized (blockState) {
|
||||||
|
Utility.writeToFile(getMetaFilename(), new Gson().toJson(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteThisFromFile() {
|
||||||
|
new File(getMetaFilename()).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path of the meta file
|
||||||
|
*
|
||||||
|
* @return the path to the meta file
|
||||||
|
*/
|
||||||
|
private String getMetaFilename() {
|
||||||
|
return location + "/" + name + ".giga";
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getDownloadedFile() {
|
||||||
|
return new File(location, name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,166 +13,165 @@ import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||||
* Runnable to download blocks of a file until the file is completely downloaded,
|
* Runnable to download blocks of a file until the file is completely downloaded,
|
||||||
* an error occurs or the process is stopped.
|
* an error occurs or the process is stopped.
|
||||||
*/
|
*/
|
||||||
public class DownloadRunnable implements Runnable
|
public class DownloadRunnable implements Runnable {
|
||||||
{
|
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
||||||
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
|
||||||
|
private final DownloadMission mMission;
|
||||||
private final DownloadMission mMission;
|
private final int mId;
|
||||||
private final int mId;
|
|
||||||
|
public DownloadRunnable(DownloadMission mission, int id) {
|
||||||
public DownloadRunnable(DownloadMission mission, int id) {
|
if (mission == null) throw new NullPointerException("mission is null");
|
||||||
if(mission == null) throw new NullPointerException("mission is null");
|
mMission = mission;
|
||||||
mMission = mission;
|
mId = id;
|
||||||
mId = id;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void run() {
|
||||||
public void run() {
|
boolean retry = mMission.recovered;
|
||||||
boolean retry = mMission.recovered;
|
long position = mMission.getPosition(mId);
|
||||||
long position = mMission.getPosition(mId);
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, mId + ":default pos " + position);
|
||||||
Log.d(TAG, mId + ":default pos " + position);
|
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
||||||
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
}
|
||||||
}
|
|
||||||
|
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
|
||||||
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
|
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
mMission.pause();
|
||||||
mMission.pause();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
if (DEBUG && retry) {
|
||||||
if (DEBUG && retry) {
|
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
|
||||||
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
|
}
|
||||||
}
|
|
||||||
|
// Wait for an unblocked position
|
||||||
// Wait for an unblocked position
|
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
|
||||||
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, mId + ":position " + position + " preserved, passing");
|
||||||
Log.d(TAG, mId + ":position " + position + " preserved, passing");
|
}
|
||||||
}
|
|
||||||
|
position++;
|
||||||
position++;
|
}
|
||||||
}
|
|
||||||
|
retry = false;
|
||||||
retry = false;
|
|
||||||
|
if (position >= mMission.blocks) {
|
||||||
if (position >= mMission.blocks) {
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, mId + ":preserving position " + position);
|
||||||
Log.d(TAG, mId + ":preserving position " + position);
|
}
|
||||||
}
|
|
||||||
|
mMission.preserveBlock(position);
|
||||||
mMission.preserveBlock(position);
|
mMission.setPosition(mId, position);
|
||||||
mMission.setPosition(mId, position);
|
|
||||||
|
long start = position * DownloadManager.BLOCK_SIZE;
|
||||||
long start = position * DownloadManager.BLOCK_SIZE;
|
long end = start + DownloadManager.BLOCK_SIZE - 1;
|
||||||
long end = start + DownloadManager.BLOCK_SIZE - 1;
|
|
||||||
|
if (end >= mMission.length) {
|
||||||
if (end >= mMission.length) {
|
end = mMission.length - 1;
|
||||||
end = mMission.length - 1;
|
}
|
||||||
}
|
|
||||||
|
HttpURLConnection conn = null;
|
||||||
HttpURLConnection conn = null;
|
|
||||||
|
int total = 0;
|
||||||
int total = 0;
|
|
||||||
|
try {
|
||||||
try {
|
URL url = new URL(mMission.url);
|
||||||
URL url = new URL(mMission.url);
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
conn = (HttpURLConnection) url.openConnection();
|
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
|
||||||
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
|
||||||
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
|
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
||||||
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
}
|
||||||
}
|
|
||||||
|
// A server may be ignoring the range request
|
||||||
// A server may be ignoring the range request
|
if (conn.getResponseCode() != 206) {
|
||||||
if (conn.getResponseCode() != 206) {
|
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||||
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||||
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
|
||||||
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
|
}
|
||||||
}
|
|
||||||
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
||||||
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
f.seek(start);
|
||||||
f.seek(start);
|
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
||||||
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
byte[] buf = new byte[512];
|
||||||
byte[] buf = new byte[512];
|
|
||||||
|
while (start < end && mMission.running) {
|
||||||
while (start < end && mMission.running) {
|
int len = ipt.read(buf, 0, 512);
|
||||||
int len = ipt.read(buf, 0, 512);
|
|
||||||
|
if (len == -1) {
|
||||||
if (len == -1) {
|
break;
|
||||||
break;
|
} else {
|
||||||
} else {
|
start += len;
|
||||||
start += len;
|
total += len;
|
||||||
total += len;
|
f.write(buf, 0, len);
|
||||||
f.write(buf, 0, len);
|
notifyProgress(len);
|
||||||
notifyProgress(len);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (DEBUG && mMission.running) {
|
||||||
if (DEBUG && mMission.running) {
|
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
|
||||||
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
|
}
|
||||||
}
|
|
||||||
|
f.close();
|
||||||
f.close();
|
ipt.close();
|
||||||
ipt.close();
|
|
||||||
|
// TODO We should save progress for each thread
|
||||||
// TODO We should save progress for each thread
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
// TODO Retry count limit & notify error
|
||||||
// TODO Retry count limit & notify error
|
retry = true;
|
||||||
retry = true;
|
|
||||||
|
notifyProgress(-total);
|
||||||
notifyProgress(-total);
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
||||||
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, "thread " + mId + " exited main loop");
|
||||||
Log.d(TAG, "thread " + mId + " exited main loop");
|
}
|
||||||
}
|
|
||||||
|
if (mMission.errCode == -1 && mMission.running) {
|
||||||
if (mMission.errCode == -1 && mMission.running) {
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, "no error has happened, notifying");
|
||||||
Log.d(TAG, "no error has happened, notifying");
|
}
|
||||||
}
|
notifyFinished();
|
||||||
notifyFinished();
|
}
|
||||||
}
|
|
||||||
|
if (DEBUG && !mMission.running) {
|
||||||
if (DEBUG && !mMission.running) {
|
Log.d(TAG, "The mission has been paused. Passing.");
|
||||||
Log.d(TAG, "The mission has been paused. Passing.");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void notifyProgress(final long len) {
|
||||||
private void notifyProgress(final long len) {
|
synchronized (mMission) {
|
||||||
synchronized (mMission) {
|
mMission.notifyProgress(len);
|
||||||
mMission.notifyProgress(len);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void notifyError(final int err) {
|
||||||
private void notifyError(final int err) {
|
synchronized (mMission) {
|
||||||
synchronized (mMission) {
|
mMission.notifyError(err);
|
||||||
mMission.notifyError(err);
|
mMission.pause();
|
||||||
mMission.pause();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void notifyFinished() {
|
||||||
private void notifyFinished() {
|
synchronized (mMission) {
|
||||||
synchronized (mMission) {
|
mMission.notifyFinished();
|
||||||
mMission.notifyFinished();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,70 +6,69 @@ import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
// Single-threaded fallback mode
|
// Single-threaded fallback mode
|
||||||
public class DownloadRunnableFallback implements Runnable
|
public class DownloadRunnableFallback implements Runnable {
|
||||||
{
|
private final DownloadMission mMission;
|
||||||
private final DownloadMission mMission;
|
//private int mId;
|
||||||
//private int mId;
|
|
||||||
|
|
||||||
public DownloadRunnableFallback(DownloadMission mission) {
|
|
||||||
if(mission == null) throw new NullPointerException("mission is null");
|
|
||||||
//mId = id;
|
|
||||||
mMission = mission;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public DownloadRunnableFallback(DownloadMission mission) {
|
||||||
public void run() {
|
if (mission == null) throw new NullPointerException("mission is null");
|
||||||
try {
|
//mId = id;
|
||||||
URL url = new URL(mMission.url);
|
mMission = mission;
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
}
|
||||||
|
|
||||||
if (conn.getResponseCode() != 200 && conn.getResponseCode() != 206) {
|
|
||||||
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
|
||||||
} else {
|
|
||||||
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
|
||||||
f.seek(0);
|
|
||||||
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
|
||||||
byte[] buf = new byte[512];
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
while ((len = ipt.read(buf, 0, 512)) != -1 && mMission.running) {
|
|
||||||
f.write(buf, 0, len);
|
|
||||||
notifyProgress(len);
|
|
||||||
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
f.close();
|
|
||||||
ipt.close();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
notifyError(DownloadMission.ERROR_UNKNOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mMission.errCode == -1 && mMission.running) {
|
|
||||||
notifyFinished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyProgress(final long len) {
|
|
||||||
synchronized (mMission) {
|
|
||||||
mMission.notifyProgress(len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyError(final int err) {
|
|
||||||
synchronized (mMission) {
|
|
||||||
mMission.notifyError(err);
|
|
||||||
mMission.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyFinished() {
|
@Override
|
||||||
synchronized (mMission) {
|
public void run() {
|
||||||
mMission.notifyFinished();
|
try {
|
||||||
}
|
URL url = new URL(mMission.url);
|
||||||
}
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
if (conn.getResponseCode() != 200 && conn.getResponseCode() != 206) {
|
||||||
|
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||||
|
} else {
|
||||||
|
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
||||||
|
f.seek(0);
|
||||||
|
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
||||||
|
byte[] buf = new byte[512];
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
while ((len = ipt.read(buf, 0, 512)) != -1 && mMission.running) {
|
||||||
|
f.write(buf, 0, len);
|
||||||
|
notifyProgress(len);
|
||||||
|
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
f.close();
|
||||||
|
ipt.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
notifyError(DownloadMission.ERROR_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMission.errCode == -1 && mMission.running) {
|
||||||
|
notifyFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyProgress(final long len) {
|
||||||
|
synchronized (mMission) {
|
||||||
|
mMission.notifyProgress(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyError(final int err) {
|
||||||
|
synchronized (mMission) {
|
||||||
|
mMission.notifyError(err);
|
||||||
|
mMission.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyFinished() {
|
||||||
|
synchronized (mMission) {
|
||||||
|
mMission.notifyFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all values of the download mission as ContentValues.
|
* Returns all values of the download mission as ContentValues.
|
||||||
|
*
|
||||||
* @param downloadMission the download mission
|
* @param downloadMission the download mission
|
||||||
* @return the content values
|
* @return the content values
|
||||||
*/
|
*/
|
||||||
|
@ -88,7 +89,7 @@ public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DownloadMission getMissionFromCursor(Cursor cursor) {
|
public static DownloadMission getMissionFromCursor(Cursor cursor) {
|
||||||
if(cursor == null) throw new NullPointerException("cursor is null");
|
if (cursor == null) throw new NullPointerException("cursor is null");
|
||||||
int pos;
|
int pos;
|
||||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME));
|
String name = cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME));
|
||||||
String location = cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCATION));
|
String location = cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCATION));
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||||
null, null, null, DownloadMissionSQLiteHelper.KEY_TIMESTAMP);
|
null, null, null, DownloadMissionSQLiteHelper.KEY_TIMESTAMP);
|
||||||
|
|
||||||
int count = cursor.getCount();
|
int count = cursor.getCount();
|
||||||
if(count == 0) return new ArrayList<>();
|
if (count == 0) return new ArrayList<>();
|
||||||
result = new ArrayList<>(count);
|
result = new ArrayList<>(count);
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
result.add(DownloadMissionSQLiteHelper.getMissionFromCursor(cursor));
|
result.add(DownloadMissionSQLiteHelper.getMissionFromCursor(cursor));
|
||||||
|
@ -47,7 +47,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMission(DownloadMission downloadMission) {
|
public void addMission(DownloadMission downloadMission) {
|
||||||
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
|
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
|
||||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
||||||
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
||||||
database.insert(MISSIONS_TABLE_NAME, null, values);
|
database.insert(MISSIONS_TABLE_NAME, null, values);
|
||||||
|
@ -55,25 +55,25 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateMission(DownloadMission downloadMission) {
|
public void updateMission(DownloadMission downloadMission) {
|
||||||
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
|
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
|
||||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
||||||
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
||||||
String whereClause = KEY_LOCATION+ " = ? AND " +
|
String whereClause = KEY_LOCATION + " = ? AND " +
|
||||||
KEY_NAME + " = ?";
|
KEY_NAME + " = ?";
|
||||||
int rowsAffected = database.update(MISSIONS_TABLE_NAME, values,
|
int rowsAffected = database.update(MISSIONS_TABLE_NAME, values,
|
||||||
whereClause, new String[]{downloadMission.location, downloadMission.name});
|
whereClause, new String[]{downloadMission.location, downloadMission.name});
|
||||||
if(rowsAffected != 1) {
|
if (rowsAffected != 1) {
|
||||||
Log.e(TAG, "Expected 1 row to be affected by update but got " + rowsAffected);
|
Log.e(TAG, "Expected 1 row to be affected by update but got " + rowsAffected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteMission(DownloadMission downloadMission) {
|
public void deleteMission(DownloadMission downloadMission) {
|
||||||
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
|
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
|
||||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
||||||
database.delete(MISSIONS_TABLE_NAME,
|
database.delete(MISSIONS_TABLE_NAME,
|
||||||
KEY_LOCATION + " = ? AND " +
|
KEY_LOCATION + " = ? AND " +
|
||||||
KEY_NAME + " = ?",
|
KEY_NAME + " = ?",
|
||||||
new String[]{downloadMission.location, downloadMission.name});
|
new String[]{downloadMission.location, downloadMission.name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -16,6 +15,7 @@ import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.support.v4.app.NotificationCompat.Builder;
|
import android.support.v4.app.NotificationCompat.Builder;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.content.PermissionChecker;
|
import android.support.v4.content.PermissionChecker;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -34,237 +34,235 @@ import us.shandian.giga.get.sqlite.SQLiteDownloadDataSource;
|
||||||
|
|
||||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||||
|
|
||||||
public class DownloadManagerService extends Service
|
public class DownloadManagerService extends Service {
|
||||||
{
|
|
||||||
|
|
||||||
private static final String TAG = DownloadManagerService.class.getSimpleName();
|
private static final String TAG = DownloadManagerService.class.getSimpleName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message code of update messages stored as {@link Message#what}.
|
* Message code of update messages stored as {@link Message#what}.
|
||||||
*/
|
*/
|
||||||
private static final int UPDATE_MESSAGE = 0;
|
private static final int UPDATE_MESSAGE = 0;
|
||||||
private static final int NOTIFICATION_ID = 1000;
|
private static final int NOTIFICATION_ID = 1000;
|
||||||
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
|
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
|
||||||
private static final String EXTRA_LOCATION = "DownloadManagerService.extra.location";
|
private static final String EXTRA_LOCATION = "DownloadManagerService.extra.location";
|
||||||
private static final String EXTRA_IS_AUDIO = "DownloadManagerService.extra.is_audio";
|
private static final String EXTRA_IS_AUDIO = "DownloadManagerService.extra.is_audio";
|
||||||
private static final String EXTRA_THREADS = "DownloadManagerService.extra.threads";
|
private static final String EXTRA_THREADS = "DownloadManagerService.extra.threads";
|
||||||
|
|
||||||
|
|
||||||
private DMBinder mBinder;
|
private DMBinder mBinder;
|
||||||
private DownloadManager mManager;
|
private DownloadManager mManager;
|
||||||
private Notification mNotification;
|
private Notification mNotification;
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private long mLastTimeStamp = System.currentTimeMillis();
|
private long mLastTimeStamp = System.currentTimeMillis();
|
||||||
private DownloadDataSource mDataSource;
|
private DownloadDataSource mDataSource;
|
||||||
|
|
||||||
|
|
||||||
|
private MissionListener missionListener = new MissionListener();
|
||||||
private MissionListener missionListener = new MissionListener();
|
|
||||||
|
|
||||||
|
|
||||||
private void notifyMediaScanner(DownloadMission mission) {
|
private void notifyMediaScanner(DownloadMission mission) {
|
||||||
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
|
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
|
||||||
// notify media scanner on downloaded media file ...
|
// notify media scanner on downloaded media file ...
|
||||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "onCreate");
|
Log.d(TAG, "onCreate");
|
||||||
}
|
}
|
||||||
|
|
||||||
mBinder = new DMBinder();
|
mBinder = new DMBinder();
|
||||||
if(mDataSource == null) {
|
if (mDataSource == null) {
|
||||||
mDataSource = new SQLiteDownloadDataSource(this);
|
mDataSource = new SQLiteDownloadDataSource(this);
|
||||||
}
|
}
|
||||||
if (mManager == null) {
|
if (mManager == null) {
|
||||||
ArrayList<String> paths = new ArrayList<>(2);
|
ArrayList<String> paths = new ArrayList<>(2);
|
||||||
paths.add(NewPipeSettings.getVideoDownloadPath(this));
|
paths.add(NewPipeSettings.getVideoDownloadPath(this));
|
||||||
paths.add(NewPipeSettings.getAudioDownloadPath(this));
|
paths.add(NewPipeSettings.getAudioDownloadPath(this));
|
||||||
mManager = new DownloadManagerImpl(paths, mDataSource);
|
mManager = new DownloadManagerImpl(paths, mDataSource);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "mManager == null");
|
Log.d(TAG, "mManager == null");
|
||||||
Log.d(TAG, "Download directory: " + paths);
|
Log.d(TAG, "Download directory: " + paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent i = new Intent();
|
Intent i = new Intent();
|
||||||
i.setAction(Intent.ACTION_MAIN);
|
i.setAction(Intent.ACTION_MAIN);
|
||||||
i.setClass(this, DownloadActivity.class);
|
i.setClass(this, DownloadActivity.class);
|
||||||
|
|
||||||
Drawable icon = this.getResources().getDrawable(R.mipmap.ic_launcher);
|
Drawable icon = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
|
||||||
|
|
||||||
Builder builder = new Builder(this)
|
Builder builder = new Builder(this)
|
||||||
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
|
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
||||||
.setContentTitle(getString(R.string.msg_running))
|
.setContentTitle(getString(R.string.msg_running))
|
||||||
.setContentText(getString(R.string.msg_running_detail));
|
.setContentText(getString(R.string.msg_running_detail));
|
||||||
|
|
||||||
PendingIntent pendingIntent =
|
PendingIntent pendingIntent =
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
new Intent(this, DownloadActivity.class)
|
new Intent(this, DownloadActivity.class)
|
||||||
.setAction(DownloadActivity.INTENT_LIST),
|
.setAction(DownloadActivity.INTENT_LIST),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.setContentIntent(pendingIntent);
|
builder.setContentIntent(pendingIntent);
|
||||||
|
|
||||||
mNotification = builder.build();
|
mNotification = builder.build();
|
||||||
|
|
||||||
HandlerThread thread = new HandlerThread("ServiceMessenger");
|
HandlerThread thread = new HandlerThread("ServiceMessenger");
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
mHandler = new Handler(thread.getLooper()) {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case UPDATE_MESSAGE: {
|
|
||||||
int runningCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < mManager.getCount(); i++) {
|
mHandler = new Handler(thread.getLooper()) {
|
||||||
if (mManager.getMission(i).running) {
|
@Override
|
||||||
runningCount++;
|
public void handleMessage(Message msg) {
|
||||||
}
|
switch (msg.what) {
|
||||||
}
|
case UPDATE_MESSAGE: {
|
||||||
updateState(runningCount);
|
int runningCount = 0;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startMissionAsync(final String url, final String location, final String name,
|
for (int i = 0; i < mManager.getCount(); i++) {
|
||||||
final boolean isAudio, final int threads) {
|
if (mManager.getMission(i).running) {
|
||||||
mHandler.post(new Runnable() {
|
runningCount++;
|
||||||
@Override
|
}
|
||||||
public void run() {
|
}
|
||||||
int missionId = mManager.startMission(url, location, name, isAudio, threads);
|
updateState(runningCount);
|
||||||
mBinder.onMissionAdded(mManager.getMission(missionId));
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Starting");
|
|
||||||
}
|
|
||||||
Log.i(TAG, "Got intent: " + intent);
|
|
||||||
String action = intent.getAction();
|
|
||||||
if(action != null && action.equals(Intent.ACTION_RUN)) {
|
|
||||||
String name = intent.getStringExtra(EXTRA_NAME);
|
|
||||||
String location = intent.getStringExtra(EXTRA_LOCATION);
|
|
||||||
int threads = intent.getIntExtra(EXTRA_THREADS, 1);
|
|
||||||
boolean isAudio = intent.getBooleanExtra(EXTRA_IS_AUDIO, false);
|
|
||||||
String url = intent.getDataString();
|
|
||||||
startMissionAsync(url, location, name, isAudio, threads);
|
|
||||||
}
|
|
||||||
return START_NOT_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void startMissionAsync(final String url, final String location, final String name,
|
||||||
public void onDestroy() {
|
final boolean isAudio, final int threads) {
|
||||||
super.onDestroy();
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
if (DEBUG) {
|
public void run() {
|
||||||
Log.d(TAG, "Destroying");
|
int missionId = mManager.startMission(url, location, name, isAudio, threads);
|
||||||
}
|
mBinder.onMissionAdded(mManager.getMission(missionId));
|
||||||
|
}
|
||||||
for (int i = 0; i < mManager.getCount(); i++) {
|
});
|
||||||
mManager.pauseMission(i);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
stopForeground(true);
|
@Override
|
||||||
}
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
if (DEBUG) {
|
||||||
@Override
|
Log.d(TAG, "Starting");
|
||||||
public IBinder onBind(Intent intent) {
|
}
|
||||||
int permissionCheck;
|
Log.i(TAG, "Got intent: " + intent);
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
String action = intent.getAction();
|
||||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
if (action != null && action.equals(Intent.ACTION_RUN)) {
|
||||||
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
String name = intent.getStringExtra(EXTRA_NAME);
|
||||||
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
|
String location = intent.getStringExtra(EXTRA_LOCATION);
|
||||||
}
|
int threads = intent.getIntExtra(EXTRA_THREADS, 1);
|
||||||
}
|
boolean isAudio = intent.getBooleanExtra(EXTRA_IS_AUDIO, false);
|
||||||
|
String url = intent.getDataString();
|
||||||
|
startMissionAsync(url, location, name, isAudio, threads);
|
||||||
|
}
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
@Override
|
||||||
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
public void onDestroy() {
|
||||||
Toast.makeText(this, "Permission denied (write)", Toast.LENGTH_SHORT).show();
|
super.onDestroy();
|
||||||
}
|
|
||||||
|
|
||||||
return mBinder;
|
if (DEBUG) {
|
||||||
}
|
Log.d(TAG, "Destroying");
|
||||||
|
}
|
||||||
|
|
||||||
private void postUpdateMessage() {
|
for (int i = 0; i < mManager.getCount(); i++) {
|
||||||
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
|
mManager.pauseMission(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState(int runningCount) {
|
|
||||||
if (runningCount == 0) {
|
|
||||||
stopForeground(true);
|
|
||||||
} else {
|
|
||||||
startForeground(NOTIFICATION_ID, mNotification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startMission(Context context, String url, String location, String name, boolean isAudio, int threads) {
|
stopForeground(true);
|
||||||
Intent intent = new Intent(context, DownloadManagerService.class);
|
}
|
||||||
intent.setAction(Intent.ACTION_RUN);
|
|
||||||
intent.setData(Uri.parse(url));
|
@Override
|
||||||
intent.putExtra(EXTRA_NAME, name);
|
public IBinder onBind(Intent intent) {
|
||||||
intent.putExtra(EXTRA_LOCATION, location);
|
int permissionCheck;
|
||||||
intent.putExtra(EXTRA_IS_AUDIO, isAudio);
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
intent.putExtra(EXTRA_THREADS, threads);
|
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
context.startService(intent);
|
if (permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
||||||
}
|
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||||
|
if (permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
||||||
|
Toast.makeText(this, "Permission denied (write)", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postUpdateMessage() {
|
||||||
|
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState(int runningCount) {
|
||||||
|
if (runningCount == 0) {
|
||||||
|
stopForeground(true);
|
||||||
|
} else {
|
||||||
|
startForeground(NOTIFICATION_ID, mNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startMission(Context context, String url, String location, String name, boolean isAudio, int threads) {
|
||||||
|
Intent intent = new Intent(context, DownloadManagerService.class);
|
||||||
|
intent.setAction(Intent.ACTION_RUN);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
intent.putExtra(EXTRA_NAME, name);
|
||||||
|
intent.putExtra(EXTRA_LOCATION, location);
|
||||||
|
intent.putExtra(EXTRA_IS_AUDIO, isAudio);
|
||||||
|
intent.putExtra(EXTRA_THREADS, threads);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MissionListener implements DownloadMission.MissionListener {
|
private class MissionListener implements DownloadMission.MissionListener {
|
||||||
@Override
|
@Override
|
||||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long delta = now - mLastTimeStamp;
|
long delta = now - mLastTimeStamp;
|
||||||
if (delta > 2000) {
|
if (delta > 2000) {
|
||||||
postUpdateMessage();
|
postUpdateMessage();
|
||||||
mLastTimeStamp = now;
|
mLastTimeStamp = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFinish(DownloadMission downloadMission) {
|
public void onFinish(DownloadMission downloadMission) {
|
||||||
postUpdateMessage();
|
postUpdateMessage();
|
||||||
notifyMediaScanner(downloadMission);
|
notifyMediaScanner(downloadMission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(DownloadMission downloadMission, int errCode) {
|
public void onError(DownloadMission downloadMission, int errCode) {
|
||||||
postUpdateMessage();
|
postUpdateMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wrapper of DownloadManager
|
// Wrapper of DownloadManager
|
||||||
public class DMBinder extends Binder {
|
public class DMBinder extends Binder {
|
||||||
public DownloadManager getDownloadManager() {
|
public DownloadManager getDownloadManager() {
|
||||||
return mManager;
|
return mManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMissionAdded(DownloadMission mission) {
|
public void onMissionAdded(DownloadMission mission) {
|
||||||
mission.addListener(missionListener);
|
mission.addListener(missionListener);
|
||||||
postUpdateMessage();
|
postUpdateMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMissionRemoved(DownloadMission mission) {
|
public void onMissionRemoved(DownloadMission mission) {
|
||||||
mission.removeListener(missionListener);
|
mission.removeListener(missionListener);
|
||||||
postUpdateMessage();
|
postUpdateMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.v4.content.FileProvider;
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -18,14 +20,13 @@ import android.widget.ImageView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import us.shandian.giga.get.DownloadManager;
|
import us.shandian.giga.get.DownloadManager;
|
||||||
import us.shandian.giga.get.DownloadMission;
|
import us.shandian.giga.get.DownloadMission;
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
|
@ -35,341 +36,340 @@ import us.shandian.giga.util.Utility;
|
||||||
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
|
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
|
||||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||||
|
|
||||||
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder>
|
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder> {
|
||||||
{
|
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
|
||||||
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
|
private static final String TAG = "MissionAdapter";
|
||||||
private static final String TAG = "MissionAdapter";
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ALGORITHMS.put(R.id.md5, "MD5");
|
ALGORITHMS.put(R.id.md5, "MD5");
|
||||||
ALGORITHMS.put(R.id.sha1, "SHA1");
|
ALGORITHMS.put(R.id.sha1, "SHA1");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private LayoutInflater mInflater;
|
private LayoutInflater mInflater;
|
||||||
private DownloadManager mManager;
|
private DownloadManager mManager;
|
||||||
private DownloadManagerService.DMBinder mBinder;
|
private DownloadManagerService.DMBinder mBinder;
|
||||||
private int mLayout;
|
private int mLayout;
|
||||||
|
|
||||||
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
|
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mManager = manager;
|
mManager = manager;
|
||||||
mBinder = binder;
|
mBinder = binder;
|
||||||
|
|
||||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
|
||||||
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
|
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MissionAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
final ViewHolder h = new ViewHolder(mInflater.inflate(mLayout, parent, false));
|
||||||
|
|
||||||
|
h.menu.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
buildPopup(h);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
|
||||||
public MissionAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
final ViewHolder h = new ViewHolder(mInflater.inflate(mLayout, parent, false));
|
|
||||||
|
|
||||||
h.menu.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
buildPopup(h);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*h.itemView.setOnClickListener(new View.OnClickListener() {
|
/*h.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
showDetail(h);
|
showDetail(h);
|
||||||
}
|
}
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return h;
|
||||||
public void onViewRecycled(MissionAdapter.ViewHolder h) {
|
}
|
||||||
super.onViewRecycled(h);
|
|
||||||
h.mission.removeListener(h.observer);
|
|
||||||
h.mission = null;
|
|
||||||
h.observer = null;
|
|
||||||
h.progress = null;
|
|
||||||
h.position = -1;
|
|
||||||
h.lastTimeStamp = -1;
|
|
||||||
h.lastDone = -1;
|
|
||||||
h.colorId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
|
public void onViewRecycled(MissionAdapter.ViewHolder h) {
|
||||||
DownloadMission ms = mManager.getMission(pos);
|
super.onViewRecycled(h);
|
||||||
h.mission = ms;
|
h.mission.removeListener(h.observer);
|
||||||
h.position = pos;
|
h.mission = null;
|
||||||
|
h.observer = null;
|
||||||
Utility.FileType type = Utility.getFileType(ms.name);
|
h.progress = null;
|
||||||
|
h.position = -1;
|
||||||
h.icon.setImageResource(Utility.getIconForFileType(type));
|
h.lastTimeStamp = -1;
|
||||||
h.name.setText(ms.name);
|
h.lastDone = -1;
|
||||||
h.size.setText(Utility.formatBytes(ms.length));
|
h.colorId = 0;
|
||||||
|
}
|
||||||
h.progress = new ProgressDrawable(mContext, Utility.getBackgroundForFileType(type), Utility.getForegroundForFileType(type));
|
|
||||||
h.bkg.setBackgroundDrawable(h.progress);
|
|
||||||
|
|
||||||
h.observer = new MissionObserver(this, h);
|
|
||||||
ms.addListener(h.observer);
|
|
||||||
|
|
||||||
updateProgress(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
|
||||||
return mManager.getCount();
|
DownloadMission ms = mManager.getMission(pos);
|
||||||
}
|
h.mission = ms;
|
||||||
|
h.position = pos;
|
||||||
|
|
||||||
@Override
|
Utility.FileType type = Utility.getFileType(ms.name);
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateProgress(ViewHolder h) {
|
|
||||||
updateProgress(h, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateProgress(ViewHolder h, boolean finished) {
|
|
||||||
if (h.mission == null) return;
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if (h.lastTimeStamp == -1) {
|
|
||||||
h.lastTimeStamp = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h.lastDone == -1) {
|
|
||||||
h.lastDone = h.mission.done;
|
|
||||||
}
|
|
||||||
|
|
||||||
long deltaTime = now - h.lastTimeStamp;
|
|
||||||
long deltaDone = h.mission.done - h.lastDone;
|
|
||||||
|
|
||||||
if (deltaTime == 0 || deltaTime > 1000 || finished) {
|
|
||||||
if (h.mission.errCode > 0) {
|
|
||||||
h.status.setText(R.string.msg_error);
|
|
||||||
} else {
|
|
||||||
float progress = (float) h.mission.done / h.mission.length;
|
|
||||||
h.status.setText(String.format(Locale.US, "%.2f%%", progress * 100));
|
|
||||||
h.progress.setProgress(progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deltaTime > 1000 && deltaDone > 0) {
|
|
||||||
float speed = (float) deltaDone / deltaTime;
|
|
||||||
String speedStr = Utility.formatSpeed(speed * 1000);
|
|
||||||
String sizeStr = Utility.formatBytes(h.mission.length);
|
|
||||||
|
|
||||||
h.size.setText(sizeStr + " " + speedStr);
|
|
||||||
|
|
||||||
h.lastTimeStamp = now;
|
|
||||||
h.lastDone = h.mission.done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void buildPopup(final ViewHolder h) {
|
h.icon.setImageResource(Utility.getIconForFileType(type));
|
||||||
PopupMenu popup = new PopupMenu(mContext, h.menu);
|
h.name.setText(ms.name);
|
||||||
popup.inflate(R.menu.mission);
|
h.size.setText(Utility.formatBytes(ms.length));
|
||||||
|
|
||||||
Menu menu = popup.getMenu();
|
|
||||||
MenuItem start = menu.findItem(R.id.start);
|
|
||||||
MenuItem pause = menu.findItem(R.id.pause);
|
|
||||||
MenuItem view = menu.findItem(R.id.view);
|
|
||||||
MenuItem delete = menu.findItem(R.id.delete);
|
|
||||||
MenuItem checksum = menu.findItem(R.id.checksum);
|
|
||||||
|
|
||||||
// Set to false first
|
|
||||||
start.setVisible(false);
|
|
||||||
pause.setVisible(false);
|
|
||||||
view.setVisible(false);
|
|
||||||
delete.setVisible(false);
|
|
||||||
checksum.setVisible(false);
|
|
||||||
|
|
||||||
if (!h.mission.finished) {
|
|
||||||
if (!h.mission.running) {
|
|
||||||
if (h.mission.errCode == -1) {
|
|
||||||
start.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete.setVisible(true);
|
|
||||||
} else {
|
|
||||||
pause.setVisible(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
view.setVisible(true);
|
|
||||||
delete.setVisible(true);
|
|
||||||
checksum.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.start:
|
|
||||||
mManager.resumeMission(h.position);
|
|
||||||
mBinder.onMissionAdded(mManager.getMission(h.position));
|
|
||||||
return true;
|
|
||||||
case R.id.pause:
|
|
||||||
mManager.pauseMission(h.position);
|
|
||||||
mBinder.onMissionRemoved(mManager.getMission(h.position));
|
|
||||||
h.lastTimeStamp = -1;
|
|
||||||
h.lastDone = -1;
|
|
||||||
return true;
|
|
||||||
case R.id.view:
|
|
||||||
File f = new File(h.mission.location, h.mission.name);
|
|
||||||
String ext = Utility.getFileExt(h.mission.name);
|
|
||||||
|
|
||||||
Log.d(TAG, "Viewing file: " + f.getAbsolutePath() + " ext: " + ext);
|
h.progress = new ProgressDrawable(mContext, Utility.getBackgroundForFileType(type), Utility.getForegroundForFileType(type));
|
||||||
|
ViewCompat.setBackground(h.bkg, h.progress);
|
||||||
|
|
||||||
if (ext == null) {
|
h.observer = new MissionObserver(this, h);
|
||||||
Log.w(TAG, "Can't view file because it has no extension: " +
|
ms.addListener(h.observer);
|
||||||
h.mission.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.substring(1));
|
|
||||||
Log.v(TAG, "Mime: " + mime + " package: " + mContext.getApplicationContext().getPackageName() + ".provider");
|
|
||||||
if (f.exists()) {
|
|
||||||
viewFileWithFileProvider(f, mime);
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "File doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case R.id.delete:
|
|
||||||
mManager.deleteMission(h.position);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
return true;
|
|
||||||
case R.id.md5:
|
|
||||||
case R.id.sha1:
|
|
||||||
DownloadMission mission = mManager.getMission(h.position);
|
|
||||||
new ChecksumTask().execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
popup.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void viewFile(File file, String mimetype) {
|
updateProgress(h);
|
||||||
Intent intent = new Intent();
|
}
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(Uri.fromFile(file), mimetype);
|
|
||||||
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
|
|
||||||
}
|
|
||||||
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
Log.v(TAG, "Starting intent: " + intent);
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void viewFileWithFileProvider(File file, String mimetype) {
|
@Override
|
||||||
String ourPackage = mContext.getApplicationContext().getPackageName();
|
public int getItemCount() {
|
||||||
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);
|
return mManager.getCount();
|
||||||
Intent intent = new Intent();
|
}
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(uri, mimetype);
|
|
||||||
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
|
|
||||||
}
|
|
||||||
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
Log.v(TAG, "Starting intent: " + intent);
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ChecksumTask extends AsyncTask<String, Void, String> {
|
|
||||||
ProgressDialog prog;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
public long getItemId(int position) {
|
||||||
super.onPreExecute();
|
return position;
|
||||||
|
}
|
||||||
// Create dialog
|
|
||||||
prog = new ProgressDialog(mContext);
|
|
||||||
prog.setCancelable(false);
|
|
||||||
prog.setMessage(mContext.getString(R.string.msg_wait));
|
|
||||||
prog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void updateProgress(ViewHolder h) {
|
||||||
protected String doInBackground(String... params) {
|
updateProgress(h, false);
|
||||||
return Utility.checksum(params[0], params[1]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void updateProgress(ViewHolder h, boolean finished) {
|
||||||
protected void onPostExecute(String result) {
|
if (h.mission == null) return;
|
||||||
super.onPostExecute(result);
|
|
||||||
prog.dismiss();
|
|
||||||
Utility.copyToClipboard(mContext, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
public DownloadMission mission;
|
|
||||||
public int position;
|
|
||||||
|
|
||||||
public TextView status;
|
long now = System.currentTimeMillis();
|
||||||
public ImageView icon;
|
|
||||||
public TextView name;
|
|
||||||
public TextView size;
|
|
||||||
public View bkg;
|
|
||||||
public ImageView menu;
|
|
||||||
public ProgressDrawable progress;
|
|
||||||
public MissionObserver observer;
|
|
||||||
|
|
||||||
public long lastTimeStamp = -1;
|
if (h.lastTimeStamp == -1) {
|
||||||
public long lastDone = -1;
|
h.lastTimeStamp = now;
|
||||||
public int colorId;
|
}
|
||||||
|
|
||||||
public ViewHolder(View v) {
|
|
||||||
super(v);
|
|
||||||
|
|
||||||
status = Utility.findViewById(v, R.id.item_status);
|
|
||||||
icon = Utility.findViewById(v, R.id.item_icon);
|
|
||||||
name = Utility.findViewById(v, R.id.item_name);
|
|
||||||
size = Utility.findViewById(v, R.id.item_size);
|
|
||||||
bkg = Utility.findViewById(v, R.id.item_bkg);
|
|
||||||
menu = Utility.findViewById(v, R.id.item_more);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MissionObserver implements DownloadMission.MissionListener {
|
|
||||||
private MissionAdapter mAdapter;
|
|
||||||
private ViewHolder mHolder;
|
|
||||||
|
|
||||||
public MissionObserver(MissionAdapter adapter, ViewHolder holder) {
|
|
||||||
mAdapter = adapter;
|
|
||||||
mHolder = holder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
|
||||||
mAdapter.updateProgress(mHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (h.lastDone == -1) {
|
||||||
public void onFinish(DownloadMission downloadMission) {
|
h.lastDone = h.mission.done;
|
||||||
//mAdapter.mManager.deleteMission(mHolder.position);
|
}
|
||||||
// TODO Notification
|
|
||||||
//mAdapter.notifyDataSetChanged();
|
|
||||||
if (mHolder.mission != null) {
|
|
||||||
mHolder.size.setText(Utility.formatBytes(mHolder.mission.length));
|
|
||||||
mAdapter.updateProgress(mHolder, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
long deltaTime = now - h.lastTimeStamp;
|
||||||
public void onError(DownloadMission downloadMission, int errCode) {
|
long deltaDone = h.mission.done - h.lastDone;
|
||||||
mAdapter.updateProgress(mHolder);
|
|
||||||
}
|
if (deltaTime == 0 || deltaTime > 1000 || finished) {
|
||||||
|
if (h.mission.errCode > 0) {
|
||||||
}
|
h.status.setText(R.string.msg_error);
|
||||||
|
} else {
|
||||||
|
float progress = (float) h.mission.done / h.mission.length;
|
||||||
|
h.status.setText(String.format(Locale.US, "%.2f%%", progress * 100));
|
||||||
|
h.progress.setProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaTime > 1000 && deltaDone > 0) {
|
||||||
|
float speed = (float) deltaDone / deltaTime;
|
||||||
|
String speedStr = Utility.formatSpeed(speed * 1000);
|
||||||
|
String sizeStr = Utility.formatBytes(h.mission.length);
|
||||||
|
|
||||||
|
h.size.setText(sizeStr + " " + speedStr);
|
||||||
|
|
||||||
|
h.lastTimeStamp = now;
|
||||||
|
h.lastDone = h.mission.done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void buildPopup(final ViewHolder h) {
|
||||||
|
PopupMenu popup = new PopupMenu(mContext, h.menu);
|
||||||
|
popup.inflate(R.menu.mission);
|
||||||
|
|
||||||
|
Menu menu = popup.getMenu();
|
||||||
|
MenuItem start = menu.findItem(R.id.start);
|
||||||
|
MenuItem pause = menu.findItem(R.id.pause);
|
||||||
|
MenuItem view = menu.findItem(R.id.view);
|
||||||
|
MenuItem delete = menu.findItem(R.id.delete);
|
||||||
|
MenuItem checksum = menu.findItem(R.id.checksum);
|
||||||
|
|
||||||
|
// Set to false first
|
||||||
|
start.setVisible(false);
|
||||||
|
pause.setVisible(false);
|
||||||
|
view.setVisible(false);
|
||||||
|
delete.setVisible(false);
|
||||||
|
checksum.setVisible(false);
|
||||||
|
|
||||||
|
if (!h.mission.finished) {
|
||||||
|
if (!h.mission.running) {
|
||||||
|
if (h.mission.errCode == -1) {
|
||||||
|
start.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete.setVisible(true);
|
||||||
|
} else {
|
||||||
|
pause.setVisible(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view.setVisible(true);
|
||||||
|
delete.setVisible(true);
|
||||||
|
checksum.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
switch (id) {
|
||||||
|
case R.id.start:
|
||||||
|
mManager.resumeMission(h.position);
|
||||||
|
mBinder.onMissionAdded(mManager.getMission(h.position));
|
||||||
|
return true;
|
||||||
|
case R.id.pause:
|
||||||
|
mManager.pauseMission(h.position);
|
||||||
|
mBinder.onMissionRemoved(mManager.getMission(h.position));
|
||||||
|
h.lastTimeStamp = -1;
|
||||||
|
h.lastDone = -1;
|
||||||
|
return true;
|
||||||
|
case R.id.view:
|
||||||
|
File f = new File(h.mission.location, h.mission.name);
|
||||||
|
String ext = Utility.getFileExt(h.mission.name);
|
||||||
|
|
||||||
|
Log.d(TAG, "Viewing file: " + f.getAbsolutePath() + " ext: " + ext);
|
||||||
|
|
||||||
|
if (ext == null) {
|
||||||
|
Log.w(TAG, "Can't view file because it has no extension: " +
|
||||||
|
h.mission.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.substring(1));
|
||||||
|
Log.v(TAG, "Mime: " + mime + " package: " + mContext.getApplicationContext().getPackageName() + ".provider");
|
||||||
|
if (f.exists()) {
|
||||||
|
viewFileWithFileProvider(f, mime);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "File doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case R.id.delete:
|
||||||
|
mManager.deleteMission(h.position);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
return true;
|
||||||
|
case R.id.md5:
|
||||||
|
case R.id.sha1:
|
||||||
|
DownloadMission mission = mManager.getMission(h.position);
|
||||||
|
new ChecksumTask().execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void viewFile(File file, String mimetype) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(Uri.fromFile(file), mimetype);
|
||||||
|
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
Log.v(TAG, "Starting intent: " + intent);
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void viewFileWithFileProvider(File file, String mimetype) {
|
||||||
|
String ourPackage = mContext.getApplicationContext().getPackageName();
|
||||||
|
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(uri, mimetype);
|
||||||
|
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
Log.v(TAG, "Starting intent: " + intent);
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public DownloadMission mission;
|
||||||
|
public int position;
|
||||||
|
|
||||||
|
public TextView status;
|
||||||
|
public ImageView icon;
|
||||||
|
public TextView name;
|
||||||
|
public TextView size;
|
||||||
|
public View bkg;
|
||||||
|
public ImageView menu;
|
||||||
|
public ProgressDrawable progress;
|
||||||
|
public MissionObserver observer;
|
||||||
|
|
||||||
|
public long lastTimeStamp = -1;
|
||||||
|
public long lastDone = -1;
|
||||||
|
public int colorId;
|
||||||
|
|
||||||
|
public ViewHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
|
||||||
|
status = (TextView) v.findViewById(R.id.item_status);
|
||||||
|
icon = (ImageView) v.findViewById(R.id.item_icon);
|
||||||
|
name = (TextView) v.findViewById(R.id.item_name);
|
||||||
|
size = (TextView) v.findViewById(R.id.item_size);
|
||||||
|
bkg = v.findViewById(R.id.item_bkg);
|
||||||
|
menu = (ImageView) v.findViewById(R.id.item_more);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MissionObserver implements DownloadMission.MissionListener {
|
||||||
|
private MissionAdapter mAdapter;
|
||||||
|
private ViewHolder mHolder;
|
||||||
|
|
||||||
|
public MissionObserver(MissionAdapter adapter, ViewHolder holder) {
|
||||||
|
mAdapter = adapter;
|
||||||
|
mHolder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||||
|
mAdapter.updateProgress(mHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinish(DownloadMission downloadMission) {
|
||||||
|
//mAdapter.mManager.deleteMission(mHolder.position);
|
||||||
|
// TODO Notification
|
||||||
|
//mAdapter.notifyDataSetChanged();
|
||||||
|
if (mHolder.mission != null) {
|
||||||
|
mHolder.size.setText(Utility.formatBytes(mHolder.mission.length));
|
||||||
|
mAdapter.updateProgress(mHolder, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(DownloadMission downloadMission, int errCode) {
|
||||||
|
mAdapter.updateProgress(mHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChecksumTask extends AsyncTask<String, Void, String> {
|
||||||
|
ProgressDialog prog;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
|
||||||
|
// Create dialog
|
||||||
|
prog = new ProgressDialog(mContext);
|
||||||
|
prog.setCancelable(false);
|
||||||
|
prog.setMessage(mContext.getString(R.string.msg_wait));
|
||||||
|
prog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(String... params) {
|
||||||
|
return Utility.checksum(params[0], params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
prog.dismiss();
|
||||||
|
Utility.copyToClipboard(mContext, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,53 +6,55 @@ import android.graphics.ColorFilter;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.ColorRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
public class ProgressDrawable extends Drawable
|
public class ProgressDrawable extends Drawable {
|
||||||
{
|
private float mProgress;
|
||||||
private float mProgress;
|
private int mBackgroundColor, mForegroundColor;
|
||||||
private int mBackgroundColor, mForegroundColor;
|
|
||||||
|
|
||||||
public ProgressDrawable(Context context, int background, int foreground) {
|
|
||||||
this(context.getResources().getColor(background), context.getResources().getColor(foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressDrawable(int background, int foreground) {
|
|
||||||
mBackgroundColor = background;
|
|
||||||
mForegroundColor = foreground;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProgress(float progress) {
|
|
||||||
mProgress = progress;
|
|
||||||
invalidateSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ProgressDrawable(Context context, @ColorRes int background, @ColorRes int foreground) {
|
||||||
public void draw(Canvas canvas) {
|
this(ContextCompat.getColor(context, background), ContextCompat.getColor(context, foreground));
|
||||||
int width = canvas.getWidth();
|
}
|
||||||
int height = canvas.getHeight();
|
|
||||||
|
|
||||||
Paint paint = new Paint();
|
|
||||||
|
|
||||||
paint.setColor(mBackgroundColor);
|
|
||||||
canvas.drawRect(0, 0, width, height, paint);
|
|
||||||
|
|
||||||
paint.setColor(mForegroundColor);
|
|
||||||
canvas.drawRect(0, 0, (int) (mProgress * width), height, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ProgressDrawable(int background, int foreground) {
|
||||||
public void setAlpha(int alpha) {
|
mBackgroundColor = background;
|
||||||
// Unsupported
|
mForegroundColor = foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setProgress(float progress) {
|
||||||
public void setColorFilter(ColorFilter filter) {
|
mProgress = progress;
|
||||||
// Unsupported
|
invalidateSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOpacity() {
|
public void draw(@NonNull Canvas canvas) {
|
||||||
return PixelFormat.OPAQUE;
|
int width = canvas.getWidth();
|
||||||
}
|
int height = canvas.getHeight();
|
||||||
|
|
||||||
|
Paint paint = new Paint();
|
||||||
|
|
||||||
|
paint.setColor(mBackgroundColor);
|
||||||
|
canvas.drawRect(0, 0, width, height, paint);
|
||||||
|
|
||||||
|
paint.setColor(mForegroundColor);
|
||||||
|
canvas.drawRect(0, 0, (int) (mProgress * width), height, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
// Unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter filter) {
|
||||||
|
// Unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.OPAQUE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
package us.shandian.giga.ui.common;
|
package us.shandian.giga.ui.common;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import us.shandian.giga.util.Utility;
|
|
||||||
|
|
||||||
public abstract class ToolbarActivity extends ActionBarActivity
|
public abstract class ToolbarActivity extends ActionBarActivity {
|
||||||
{
|
protected Toolbar mToolbar;
|
||||||
protected Toolbar mToolbar;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(getLayoutResource());
|
setContentView(getLayoutResource());
|
||||||
|
|
||||||
mToolbar = Utility.findViewById(this, R.id.toolbar);
|
mToolbar = (Toolbar) this.findViewById(R.id.toolbar);
|
||||||
|
|
||||||
setSupportActionBar(mToolbar);
|
setSupportActionBar(mToolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int getLayoutResource();
|
protected abstract int getLayoutResource();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ package us.shandian.giga.ui.fragment;
|
||||||
import us.shandian.giga.get.DownloadManager;
|
import us.shandian.giga.get.DownloadManager;
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
|
|
||||||
public class AllMissionsFragment extends MissionsFragment
|
public class AllMissionsFragment extends MissionsFragment {
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
|
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
|
||||||
return binder.getDownloadManager();
|
return binder.getDownloadManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,126 +23,128 @@ import org.schabi.newpipe.R;
|
||||||
import us.shandian.giga.get.DownloadManager;
|
import us.shandian.giga.get.DownloadManager;
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
import us.shandian.giga.ui.adapter.MissionAdapter;
|
import us.shandian.giga.ui.adapter.MissionAdapter;
|
||||||
import us.shandian.giga.util.Utility;
|
|
||||||
|
|
||||||
public abstract class MissionsFragment extends Fragment
|
public abstract class MissionsFragment extends Fragment {
|
||||||
{
|
private DownloadManager mManager;
|
||||||
private DownloadManager mManager;
|
private DownloadManagerService.DMBinder mBinder;
|
||||||
private DownloadManagerService.DMBinder mBinder;
|
|
||||||
|
|
||||||
private SharedPreferences mPrefs;
|
|
||||||
private boolean mLinear;
|
|
||||||
private MenuItem mSwitch;
|
|
||||||
|
|
||||||
private RecyclerView mList;
|
|
||||||
private MissionAdapter mAdapter;
|
|
||||||
private GridLayoutManager mGridManager;
|
|
||||||
private LinearLayoutManager mLinearManager;
|
|
||||||
private Context mActivity;
|
|
||||||
|
|
||||||
private ServiceConnection mConnection = new ServiceConnection() {
|
|
||||||
|
|
||||||
@Override
|
private SharedPreferences mPrefs;
|
||||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
private boolean mLinear;
|
||||||
mBinder = (DownloadManagerService.DMBinder) binder;
|
private MenuItem mSwitch;
|
||||||
mManager = setupDownloadManager(mBinder);
|
|
||||||
updateList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private RecyclerView mList;
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
private MissionAdapter mAdapter;
|
||||||
// What to do?
|
private GridLayoutManager mGridManager;
|
||||||
}
|
private LinearLayoutManager mLinearManager;
|
||||||
|
private Context mActivity;
|
||||||
|
|
||||||
|
private ServiceConnection mConnection = new ServiceConnection() {
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||||
View v = inflater.inflate(R.layout.missions, container, false);
|
mBinder = (DownloadManagerService.DMBinder) binder;
|
||||||
|
mManager = setupDownloadManager(mBinder);
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
|
||||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
@Override
|
||||||
mLinear = mPrefs.getBoolean("linear", false);
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
// What to do?
|
||||||
|
}
|
||||||
|
|
||||||
// Bind the service
|
|
||||||
Intent i = new Intent();
|
|
||||||
i.setClass(getActivity(), DownloadManagerService.class);
|
|
||||||
getActivity().bindService(i, mConnection, Context.BIND_AUTO_CREATE);
|
|
||||||
|
|
||||||
// Views
|
|
||||||
mList = Utility.findViewById(v, R.id.mission_recycler);
|
|
||||||
|
|
||||||
// Init
|
|
||||||
mGridManager = new GridLayoutManager(getActivity(), 2);
|
|
||||||
mLinearManager = new LinearLayoutManager(getActivity());
|
|
||||||
mList.setLayoutManager(mGridManager);
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Added in API level 23. */
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context activity) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
super.onAttach(activity);
|
View v = inflater.inflate(R.layout.missions, container, false);
|
||||||
|
|
||||||
// Bug: in api< 23 this is never called
|
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
// so mActivity=null
|
mLinear = mPrefs.getBoolean("linear", false);
|
||||||
// so app crashes with nullpointer exception
|
|
||||||
mActivity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** deprecated in API level 23,
|
// Bind the service
|
||||||
* but must remain to allow compatibility with api<23 */
|
Intent i = new Intent();
|
||||||
@Override
|
i.setClass(getActivity(), DownloadManagerService.class);
|
||||||
public void onAttach(Activity activity) {
|
getActivity().bindService(i, mConnection, Context.BIND_AUTO_CREATE);
|
||||||
super.onAttach(activity);
|
|
||||||
|
|
||||||
mActivity = activity;
|
// Views
|
||||||
}
|
mList = (RecyclerView) v.findViewById(R.id.mission_recycler);
|
||||||
|
|
||||||
@Override
|
// Init
|
||||||
public void onDestroyView() {
|
mGridManager = new GridLayoutManager(getActivity(), 2);
|
||||||
super.onDestroyView();
|
mLinearManager = new LinearLayoutManager(getActivity());
|
||||||
getActivity().unbindService(mConnection);
|
mList.setLayoutManager(mGridManager);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
setHasOptionsMenu(true);
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
return super.onOptionsItemSelected(item);
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Added in API level 23.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context activity) {
|
||||||
|
super.onAttach(activity);
|
||||||
|
|
||||||
|
// Bug: in api< 23 this is never called
|
||||||
|
// so mActivity=null
|
||||||
|
// so app crashes with nullpointer exception
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deprecated in API level 23,
|
||||||
|
* but must remain to allow compatibility with api<23
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity) {
|
||||||
|
super.onAttach(activity);
|
||||||
|
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
getActivity().unbindService(mConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
|
||||||
/*switch (item.getItemId()) {
|
/*switch (item.getItemId()) {
|
||||||
case R.id.switch_mode:
|
case R.id.switch_mode:
|
||||||
mLinear = !mLinear;
|
mLinear = !mLinear;
|
||||||
updateList();
|
updateList();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyChange() {
|
public void notifyChange() {
|
||||||
mAdapter.notifyDataSetChanged();
|
mAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateList() {
|
private void updateList() {
|
||||||
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
|
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
|
||||||
|
|
||||||
if (mLinear) {
|
if (mLinear) {
|
||||||
mList.setLayoutManager(mLinearManager);
|
mList.setLayoutManager(mLinearManager);
|
||||||
} else {
|
} else {
|
||||||
mList.setLayoutManager(mGridManager);
|
mList.setLayoutManager(mGridManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
mList.setAdapter(mAdapter);
|
mList.setAdapter(mAdapter);
|
||||||
|
|
||||||
if (mSwitch != null) {
|
if (mSwitch != null) {
|
||||||
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
|
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPrefs.edit().putBoolean("linear", mLinear).commit();
|
mPrefs.edit().putBoolean("linear", mLinear).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
|
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,77 +9,76 @@ import java.io.File;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
//todo: replace this by using the internal crash handler of newpipe
|
//todo: replace this by using the internal crash handler of newpipe
|
||||||
public class CrashHandler implements Thread.UncaughtExceptionHandler
|
public class CrashHandler implements Thread.UncaughtExceptionHandler {
|
||||||
{
|
public static final String CRASH_DIR = Environment.getExternalStorageDirectory().getPath() + "/GigaCrash/";
|
||||||
public static final String CRASH_DIR = Environment.getExternalStorageDirectory().getPath() + "/GigaCrash/";
|
public static final String CRASH_LOG = CRASH_DIR + "last_crash.log";
|
||||||
public static final String CRASH_LOG = CRASH_DIR + "last_crash.log";
|
public static final String CRASH_TAG = CRASH_DIR + ".crashed";
|
||||||
public static final String CRASH_TAG = CRASH_DIR + ".crashed";
|
|
||||||
|
|
||||||
private static String ANDROID = Build.VERSION.RELEASE;
|
private static String ANDROID = Build.VERSION.RELEASE;
|
||||||
private static String MODEL = Build.MODEL;
|
private static String MODEL = Build.MODEL;
|
||||||
private static String MANUFACTURER = Build.MANUFACTURER;
|
private static String MANUFACTURER = Build.MANUFACTURER;
|
||||||
|
|
||||||
public static String VERSION = "Unknown";
|
public static String VERSION = "Unknown";
|
||||||
|
|
||||||
private Thread.UncaughtExceptionHandler mPrevious;
|
private Thread.UncaughtExceptionHandler mPrevious;
|
||||||
|
|
||||||
public static void init(Context context) {
|
public static void init(Context context) {
|
||||||
try {
|
try {
|
||||||
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||||
VERSION = info.versionName + info.versionCode;
|
VERSION = info.versionName + info.versionCode;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
new CrashHandler();
|
new CrashHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CrashHandler() {
|
private CrashHandler() {
|
||||||
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
|
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
|
||||||
Thread.currentThread().setUncaughtExceptionHandler(this);
|
Thread.currentThread().setUncaughtExceptionHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||||
File f = new File(CRASH_LOG);
|
File f = new File(CRASH_LOG);
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
f.delete();
|
f.delete();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
new File(CRASH_DIR).mkdirs();
|
new File(CRASH_DIR).mkdirs();
|
||||||
f.createNewFile();
|
f.createNewFile();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintWriter p;
|
PrintWriter p;
|
||||||
try {
|
try {
|
||||||
p = new PrintWriter(f);
|
p = new PrintWriter(f);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.write("Android Version: " + ANDROID + "\n");
|
p.write("Android Version: " + ANDROID + "\n");
|
||||||
p.write("Device Model: " + MODEL + "\n");
|
p.write("Device Model: " + MODEL + "\n");
|
||||||
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
|
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
|
||||||
p.write("App Version: " + VERSION + "\n");
|
p.write("App Version: " + VERSION + "\n");
|
||||||
p.write("*********************\n");
|
p.write("*********************\n");
|
||||||
throwable.printStackTrace(p);
|
throwable.printStackTrace(p);
|
||||||
|
|
||||||
p.close();
|
p.close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new File(CRASH_TAG).createNewFile();
|
new File(CRASH_TAG).createNewFile();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPrevious != null) {
|
if (mPrevious != null) {
|
||||||
mPrevious.uncaughtException(thread, throwable);
|
mPrevious.uncaughtException(thread, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,256 +1,221 @@
|
||||||
package us.shandian.giga.util;
|
package us.shandian.giga.util;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.support.annotation.ColorRes;
|
||||||
import android.view.View;
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
public class Utility {
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
public enum FileType {
|
||||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
VIDEO,
|
||||||
|
MUSIC,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
public class Utility
|
public static String formatBytes(long bytes) {
|
||||||
{
|
if (bytes < 1024) {
|
||||||
|
return String.format("%d B", bytes);
|
||||||
public static enum FileType {
|
} else if (bytes < 1024 * 1024) {
|
||||||
VIDEO,
|
return String.format("%.2f kB", (float) bytes / 1024);
|
||||||
MUSIC,
|
} else if (bytes < 1024 * 1024 * 1024) {
|
||||||
UNKNOWN
|
return String.format("%.2f MB", (float) bytes / 1024 / 1024);
|
||||||
}
|
} else {
|
||||||
|
return String.format("%.2f GB", (float) bytes / 1024 / 1024 / 1024);
|
||||||
public static String formatBytes(long bytes) {
|
}
|
||||||
if (bytes < 1024) {
|
}
|
||||||
return String.format("%d B", bytes);
|
|
||||||
} else if (bytes < 1024 * 1024) {
|
|
||||||
return String.format("%.2f kB", (float) bytes / 1024);
|
|
||||||
} else if (bytes < 1024 * 1024 * 1024) {
|
|
||||||
return String.format("%.2f MB", (float) bytes / 1024 / 1024);
|
|
||||||
} else {
|
|
||||||
return String.format("%.2f GB", (float) bytes / 1024 / 1024 / 1024);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String formatSpeed(float speed) {
|
|
||||||
if (speed < 1024) {
|
|
||||||
return String.format("%.2f B/s", speed);
|
|
||||||
} else if (speed < 1024 * 1024) {
|
|
||||||
return String.format("%.2f kB/s", speed / 1024);
|
|
||||||
} else if (speed < 1024 * 1024 * 1024) {
|
|
||||||
return String.format("%.2f MB/s", speed / 1024 / 1024);
|
|
||||||
} else {
|
|
||||||
return String.format("%.2f GB/s", speed / 1024 / 1024 / 1024);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeToFile(String fileName, String content) {
|
|
||||||
try {
|
|
||||||
writeToFile(fileName, content.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeToFile(String fileName, byte[] content) {
|
|
||||||
File f = new File(fileName);
|
|
||||||
|
|
||||||
if (!f.exists()) {
|
|
||||||
try {
|
|
||||||
f.createNewFile();
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileOutputStream opt = new FileOutputStream(f, false);
|
|
||||||
opt.write(content, 0, content.length);
|
|
||||||
opt.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String readFromFile(String file) {
|
|
||||||
try {
|
|
||||||
File f = new File(file);
|
|
||||||
|
|
||||||
if (!f.exists() || !f.canRead()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedInputStream ipt = new BufferedInputStream(new FileInputStream(f));
|
|
||||||
|
|
||||||
byte[] buf = new byte[512];
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
while (ipt.available() > 0) {
|
|
||||||
int len = ipt.read(buf, 0, 512);
|
|
||||||
sb.append(new String(buf, 0, len, "UTF-8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ipt.close();
|
|
||||||
return sb.toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T findViewById(View v, int id) {
|
|
||||||
return (T) v.findViewById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T findViewById(Activity activity, int id) {
|
|
||||||
return (T) activity.findViewById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFileExt(String url) {
|
|
||||||
if (url.indexOf("?")>-1) {
|
|
||||||
url = url.substring(0,url.indexOf("?"));
|
|
||||||
}
|
|
||||||
if (url.lastIndexOf(".") == -1) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
String ext = url.substring(url.lastIndexOf(".") );
|
|
||||||
if (ext.indexOf("%")>-1) {
|
|
||||||
ext = ext.substring(0,ext.indexOf("%"));
|
|
||||||
}
|
|
||||||
if (ext.indexOf("/")>-1) {
|
|
||||||
ext = ext.substring(0,ext.indexOf("/"));
|
|
||||||
}
|
|
||||||
return ext.toLowerCase();
|
|
||||||
|
|
||||||
}
|
public static String formatSpeed(float speed) {
|
||||||
}
|
if (speed < 1024) {
|
||||||
|
return String.format("%.2f B/s", speed);
|
||||||
|
} else if (speed < 1024 * 1024) {
|
||||||
|
return String.format("%.2f kB/s", speed / 1024);
|
||||||
|
} else if (speed < 1024 * 1024 * 1024) {
|
||||||
|
return String.format("%.2f MB/s", speed / 1024 / 1024);
|
||||||
|
} else {
|
||||||
|
return String.format("%.2f GB/s", speed / 1024 / 1024 / 1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static FileType getFileType(String file) {
|
public static void writeToFile(String fileName, String content) {
|
||||||
if (file.endsWith(".mp3") || file.endsWith(".wav") || file.endsWith(".flac") || file.endsWith(".m4a")) {
|
try {
|
||||||
return FileType.MUSIC;
|
writeToFile(fileName, content.getBytes("UTF-8"));
|
||||||
} else if (file.endsWith(".mp4") || file.endsWith(".mpeg") || file.endsWith(".rm") || file.endsWith(".rmvb")
|
} catch (Exception e) {
|
||||||
|| file.endsWith(".flv") || file.endsWith(".webp") || file.endsWith(".webm")) {
|
|
||||||
return FileType.VIDEO;
|
|
||||||
} else {
|
|
||||||
return FileType.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean isMusicFile(String file)
|
}
|
||||||
{
|
}
|
||||||
return Utility.getFileType(file) == FileType.MUSIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean isVideoFile(String file)
|
public static void writeToFile(String fileName, byte[] content) {
|
||||||
{
|
File f = new File(fileName);
|
||||||
return Utility.getFileType(file) == FileType.VIDEO;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getBackgroundForFileType(FileType type) {
|
|
||||||
switch (type) {
|
|
||||||
case MUSIC:
|
|
||||||
return R.color.audio_left_to_load_color;
|
|
||||||
case VIDEO:
|
|
||||||
return R.color.video_left_to_load_color;
|
|
||||||
default:
|
|
||||||
return R.color.gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getForegroundForFileType(FileType type) {
|
|
||||||
switch (type) {
|
|
||||||
case MUSIC:
|
|
||||||
return R.color.audio_already_load_color;
|
|
||||||
case VIDEO:
|
|
||||||
return R.color.video_already_load_color;
|
|
||||||
default:
|
|
||||||
return R.color.gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getIconForFileType(FileType type) {
|
if (!f.exists()) {
|
||||||
switch(type) {
|
try {
|
||||||
case MUSIC:
|
f.createNewFile();
|
||||||
return R.drawable.music;
|
} catch (Exception e) {
|
||||||
case VIDEO:
|
|
||||||
return R.drawable.video;
|
|
||||||
default:
|
|
||||||
return R.drawable.video;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDirectoryAvailble(String path) {
|
}
|
||||||
File dir = new File(path);
|
}
|
||||||
return dir.exists() && dir.isDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDownloadDirectoryAvailble(Context context) {
|
try {
|
||||||
return isDirectoryAvailble(NewPipeSettings.getVideoDownloadPath(context));
|
FileOutputStream opt = new FileOutputStream(f, false);
|
||||||
}
|
opt.write(content, 0, content.length);
|
||||||
|
opt.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
public static void showDirectoryChooser(Activity activity) {
|
}
|
||||||
Intent i = new Intent(activity, FilePickerActivity.class);
|
}
|
||||||
i.setAction(Intent.ACTION_GET_CONTENT);
|
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
|
public static String readFromFile(String file) {
|
||||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
|
try {
|
||||||
i.putExtra(FilePickerActivity.EXTRA_MODE, AbstractFilePickerFragment.MODE_DIR);
|
File f = new File(file);
|
||||||
activity.startActivityForResult(i, 233);
|
|
||||||
}
|
if (!f.exists() || !f.canRead()) {
|
||||||
|
return null;
|
||||||
public static void copyToClipboard(Context context, String str) {
|
}
|
||||||
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
cm.setPrimaryClip(ClipData.newPlainText("text", str));
|
BufferedInputStream ipt = new BufferedInputStream(new FileInputStream(f));
|
||||||
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
byte[] buf = new byte[512];
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
public static String checksum(String path, String algorithm) {
|
|
||||||
MessageDigest md = null;
|
while (ipt.available() > 0) {
|
||||||
|
int len = ipt.read(buf, 0, 512);
|
||||||
try {
|
sb.append(new String(buf, 0, len, "UTF-8"));
|
||||||
md = MessageDigest.getInstance(algorithm);
|
}
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
ipt.close();
|
||||||
}
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
FileInputStream i = null;
|
return null;
|
||||||
|
}
|
||||||
try {
|
}
|
||||||
i = new FileInputStream(path);
|
|
||||||
} catch (FileNotFoundException e) {
|
@Nullable
|
||||||
throw new RuntimeException(e);
|
public static String getFileExt(String url) {
|
||||||
}
|
int index;
|
||||||
|
if ((index = url.indexOf("?")) > -1) {
|
||||||
byte[] buf = new byte[1024];
|
url = url.substring(0, index);
|
||||||
int len = 0;
|
}
|
||||||
|
|
||||||
try {
|
index = url.lastIndexOf(".");
|
||||||
while ((len = i.read(buf)) != -1) {
|
if (index == -1) {
|
||||||
md.update(buf, 0, len);
|
return null;
|
||||||
}
|
} else {
|
||||||
} catch (IOException e) {
|
String ext = url.substring(index);
|
||||||
|
if ((index = ext.indexOf("%")) > -1) {
|
||||||
}
|
ext = ext.substring(0, index);
|
||||||
|
}
|
||||||
byte[] digest = md.digest();
|
if ((index = ext.indexOf("/")) > -1) {
|
||||||
|
ext = ext.substring(0, index);
|
||||||
// HEX
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
return ext.toLowerCase();
|
||||||
for (byte b : digest) {
|
}
|
||||||
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
}
|
||||||
}
|
|
||||||
|
public static FileType getFileType(String file) {
|
||||||
return sb.toString();
|
if (file.endsWith(".mp3") || file.endsWith(".wav") || file.endsWith(".flac") || file.endsWith(".m4a")) {
|
||||||
|
return FileType.MUSIC;
|
||||||
}
|
} else if (file.endsWith(".mp4") || file.endsWith(".mpeg") || file.endsWith(".rm") || file.endsWith(".rmvb")
|
||||||
|
|| file.endsWith(".flv") || file.endsWith(".webp") || file.endsWith(".webm")) {
|
||||||
|
return FileType.VIDEO;
|
||||||
|
} else {
|
||||||
|
return FileType.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorRes
|
||||||
|
public static int getBackgroundForFileType(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case MUSIC:
|
||||||
|
return R.color.audio_left_to_load_color;
|
||||||
|
case VIDEO:
|
||||||
|
return R.color.video_left_to_load_color;
|
||||||
|
default:
|
||||||
|
return R.color.gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorRes
|
||||||
|
public static int getForegroundForFileType(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case MUSIC:
|
||||||
|
return R.color.audio_already_load_color;
|
||||||
|
case VIDEO:
|
||||||
|
return R.color.video_already_load_color;
|
||||||
|
default:
|
||||||
|
return R.color.gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
public static int getIconForFileType(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case MUSIC:
|
||||||
|
return R.drawable.music;
|
||||||
|
case VIDEO:
|
||||||
|
return R.drawable.video;
|
||||||
|
default:
|
||||||
|
return R.drawable.video;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyToClipboard(Context context, String str) {
|
||||||
|
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText("text", str));
|
||||||
|
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checksum(String path, String algorithm) {
|
||||||
|
MessageDigest md = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance(algorithm);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream i = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
i = new FileInputStream(path);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ((len = i.read(buf)) != -1) {
|
||||||
|
md.update(buf, 0, len);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] digest = md.digest();
|
||||||
|
|
||||||
|
// HEX
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : digest) {
|
||||||
|
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.RouterActivity;
|
||||||
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link ErrorActivity}
|
||||||
|
*/
|
||||||
|
public class ErrorActivityTest {
|
||||||
|
@Test
|
||||||
|
public void getReturnActivity() throws Exception {
|
||||||
|
Class<? extends Activity> returnActivity;
|
||||||
|
returnActivity = ErrorActivity.getReturnActivity(MainActivity.class);
|
||||||
|
assertEquals(MainActivity.class, returnActivity);
|
||||||
|
|
||||||
|
returnActivity = ErrorActivity.getReturnActivity(RouterActivity.class);
|
||||||
|
assertEquals(RouterActivity.class, returnActivity);
|
||||||
|
|
||||||
|
returnActivity = ErrorActivity.getReturnActivity(null);
|
||||||
|
assertNull(returnActivity);
|
||||||
|
|
||||||
|
returnActivity = ErrorActivity.getReturnActivity(Integer.class);
|
||||||
|
assertEquals(MainActivity.class, returnActivity);
|
||||||
|
|
||||||
|
returnActivity = ErrorActivity.getReturnActivity(VideoDetailFragment.class);
|
||||||
|
assertEquals(MainActivity.class, returnActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -31,18 +31,18 @@ import static org.mockito.Mockito.when;
|
||||||
public class DownloadManagerImplTest {
|
public class DownloadManagerImplTest {
|
||||||
|
|
||||||
private DownloadManagerImpl downloadManager;
|
private DownloadManagerImpl downloadManager;
|
||||||
private DownloadDataSource dowloadDataSource;
|
private DownloadDataSource downloadDataSource;
|
||||||
private ArrayList<DownloadMission> missions;
|
private ArrayList<DownloadMission> missions;
|
||||||
|
|
||||||
@org.junit.Before
|
@org.junit.Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
dowloadDataSource = mock(DownloadDataSource.class);
|
downloadDataSource = mock(DownloadDataSource.class);
|
||||||
missions = new ArrayList<>();
|
missions = new ArrayList<>();
|
||||||
for(int i = 0; i < 50; ++i){
|
for(int i = 0; i < 50; ++i){
|
||||||
missions.add(generateFinishedDownloadMission());
|
missions.add(generateFinishedDownloadMission());
|
||||||
}
|
}
|
||||||
when(dowloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), dowloadDataSource);
|
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), downloadDataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test(expected = NullPointerException.class)
|
||||||
|
@ -82,10 +82,10 @@ public class DownloadManagerImplTest {
|
||||||
missions.add(mission);
|
missions.add(mission);
|
||||||
}
|
}
|
||||||
|
|
||||||
dowloadDataSource = mock(DownloadDataSource.class);
|
downloadDataSource = mock(DownloadDataSource.class);
|
||||||
when(dowloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), dowloadDataSource);
|
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), downloadDataSource);
|
||||||
verify(dowloadDataSource, times(1)).loadMissions();
|
verify(downloadDataSource, times(1)).loadMissions();
|
||||||
|
|
||||||
assertEquals(50, downloadManager.getCount());
|
assertEquals(50, downloadManager.getCount());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue