mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-11-30 05:52:46 +01:00
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
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -5,7 +5,7 @@ READ THIS GUIDELINES CAREFULLY BEFORE CONTRIBUTING.
|
||||
|
||||
## 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
|
||||
|
||||
|
@ -10,6 +10,8 @@ android {
|
||||
targetSdkVersion 25
|
||||
versionCode 35
|
||||
versionName "0.9.8"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -31,6 +33,11 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
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.report.AcraReportSenderFactory;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
@ -59,7 +60,7 @@ public class App extends Application {
|
||||
} catch(ACRAConfigurationException ace) {
|
||||
ace.printStackTrace();
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
ErrorActivity.reportError(context,
|
||||
failReason.getCause(), null, rootView,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
||||
ErrorActivity.ErrorInfo.make(UserAction.LOAD_IMAGE,
|
||||
NewPipe.getNameOfService(serviceId), imageUri,
|
||||
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.MissionsFragment;
|
||||
import us.shandian.giga.util.CrashHandler;
|
||||
import us.shandian.giga.util.Utility;
|
||||
|
||||
public class DownloadActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
||||
|
||||
@ -125,11 +124,11 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
||||
// Create the view
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View v = inflater.inflate(R.layout.dialog_url, null);
|
||||
final EditText name = Utility.findViewById(v, R.id.file_name);
|
||||
final TextView tCount = Utility.findViewById(v, R.id.threads_count);
|
||||
final SeekBar threads = Utility.findViewById(v, R.id.threads);
|
||||
final Toolbar toolbar = Utility.findViewById(v, R.id.toolbar);
|
||||
final RadioButton audioButton = (RadioButton) Utility.findViewById(v, R.id.audio_button);
|
||||
final EditText name = (EditText) v.findViewById(R.id.file_name);
|
||||
final TextView tCount = (TextView) v.findViewById(R.id.threads_count);
|
||||
final SeekBar threads = (SeekBar) v.findViewById(R.id.threads);
|
||||
final Toolbar toolbar = (Toolbar) v.findViewById(R.id.toolbar);
|
||||
final RadioButton audioButton = (RadioButton) v.findViewById(R.id.audio_button);
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
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.channel.ChannelInfo;
|
||||
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.InfoListAdapter;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
@ -245,23 +246,12 @@ public class ChannelFragment extends BaseFragment implements ChannelExtractorWor
|
||||
});
|
||||
|
||||
channelVideosList.clearOnScrollListeners();
|
||||
channelVideosList.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
channelVideosList.addOnScrollListener(new OnScrollBelowItemsListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
//check for scroll down
|
||||
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();
|
||||
}
|
||||
public void onScrolledDown(RecyclerView recyclerView) {
|
||||
if ((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.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
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() {
|
||||
@Override
|
||||
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);
|
||||
|
@ -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() {
|
||||
super.initListeners();
|
||||
resultRecyclerView.clearOnScrollListeners();
|
||||
resultRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
resultRecyclerView.addOnScrollListener(new OnScrollBelowItemsListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
int pastVisiblesItems, visibleItemCount, totalItemCount;
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
//check for scroll down
|
||||
if (dy > 0) {
|
||||
LinearLayoutManager layoutManager = (LinearLayoutManager) resultRecyclerView.getLayoutManager();
|
||||
visibleItemCount = resultRecyclerView.getLayoutManager().getChildCount();
|
||||
totalItemCount = resultRecyclerView.getLayoutManager().getItemCount();
|
||||
pastVisiblesItems = layoutManager.findFirstVisibleItemPosition();
|
||||
|
||||
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount && !isLoading.get()) {
|
||||
pageNumber++;
|
||||
recyclerView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
infoListAdapter.showFooter(true);
|
||||
}
|
||||
});
|
||||
search(searchQuery, pageNumber);
|
||||
}
|
||||
public void onScrolledDown(RecyclerView recyclerView) {
|
||||
if(!isLoading.get()) {
|
||||
pageNumber++;
|
||||
recyclerView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
infoListAdapter.showFooter(true);
|
||||
}
|
||||
});
|
||||
search(searchQuery, pageNumber);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void reloadContent() {
|
||||
if (DEBUG) Log.d(TAG, "reloadContent() called");
|
||||
|
@ -155,7 +155,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
|
||||
//god damen f*** ANDROID SH**
|
||||
//god damn f*** ANDROID SH**
|
||||
if(holder instanceof InfoItemHolder) {
|
||||
if(header != null) {
|
||||
i--;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.report;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.sender.ReportSender;
|
||||
@ -30,9 +31,9 @@ import org.schabi.newpipe.R;
|
||||
public class AcraReportSender implements ReportSender {
|
||||
|
||||
@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.ErrorInfo.make(ErrorActivity.UI_ERROR,"none",
|
||||
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,"none",
|
||||
"App crash, UI failure", R.string.app_ui_crash));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.report;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.acra.config.ACRAConfiguration;
|
||||
import org.acra.sender.ReportSender;
|
||||
@ -28,7 +29,8 @@ import org.schabi.newpipe.report.AcraReportSender;
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import android.os.Handler;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.app.ActionBar;
|
||||
@ -71,24 +73,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
// BUNDLE TAGS
|
||||
public static final String ERROR_INFO = "error_info";
|
||||
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_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME;
|
||||
Thread globIpRangeThread;
|
||||
@ -105,11 +90,11 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
private TextView errorMessageView;
|
||||
|
||||
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,
|
||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||
final Class returnActivity, View rootView, final ErrorInfo errorInfo) {
|
||||
|
||||
if (rootView != null) {
|
||||
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
||||
@ -118,7 +103,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||
ac.returnActivity = returnAcitivty;
|
||||
ac.returnActivity = returnActivity;
|
||||
Intent intent = new Intent(context, ErrorActivity.class);
|
||||
intent.putExtra(ERROR_INFO, errorInfo);
|
||||
intent.putExtra(ERROR_LIST, elToSl(el));
|
||||
@ -128,7 +113,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
}).show();
|
||||
} else {
|
||||
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||
ac.returnActivity = returnAcitivty;
|
||||
ac.returnActivity = returnActivity;
|
||||
Intent intent = new Intent(context, ErrorActivity.class);
|
||||
intent.putExtra(ERROR_INFO, errorInfo);
|
||||
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,
|
||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||
final Class returnActivity, View rootView, final ErrorInfo errorInfo) {
|
||||
List<Throwable> el = null;
|
||||
if(e != null) {
|
||||
el = new Vector<>();
|
||||
el.add(e);
|
||||
}
|
||||
reportError(context, el, returnAcitivty, rootView, errorInfo);
|
||||
reportError(context, el, returnActivity, rootView, errorInfo);
|
||||
}
|
||||
|
||||
// async call
|
||||
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;
|
||||
if(e != null) {
|
||||
el = new Vector<>();
|
||||
el.add(e);
|
||||
}
|
||||
reportError(handler, context, el, returnAcitivty, rootView, errorInfo);
|
||||
reportError(handler, context, el, returnActivity, rootView, errorInfo);
|
||||
}
|
||||
|
||||
// async call
|
||||
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() {
|
||||
@Override
|
||||
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);
|
||||
errorList = intent.getStringArrayExtra(ERROR_LIST);
|
||||
|
||||
//importand add gurumeditaion
|
||||
// important add guru meditation
|
||||
addGuruMeditaion();
|
||||
currentTimeStamp = getCurrentTimeStamp();
|
||||
|
||||
@ -250,7 +235,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
});
|
||||
reportButton.setEnabled(false);
|
||||
|
||||
globIpRangeThread = new Thread(new IpRagneRequester());
|
||||
globIpRangeThread = new Thread(new IpRangeRequester());
|
||||
globIpRangeThread.start();
|
||||
|
||||
// normal bugreport
|
||||
@ -308,17 +293,30 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
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() {
|
||||
if (returnActivity == null) {
|
||||
Class<? extends Activity> checkedReturnActivity = getReturnActivity(returnActivity);
|
||||
if (checkedReturnActivity == null) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
Intent intent;
|
||||
if (returnActivity != null &&
|
||||
returnActivity.isAssignableFrom(Activity.class)) {
|
||||
intent = new Intent(this, returnActivity);
|
||||
} else {
|
||||
intent = new Intent(this, MainActivity.class);
|
||||
}
|
||||
Intent intent = new Intent(this, checkedReturnActivity);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
NavUtils.navigateUpTo(this, intent);
|
||||
}
|
||||
@ -376,26 +374,11 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getUserActionString(int userAction) {
|
||||
switch (userAction) {
|
||||
case REQUESTED_STREAM:
|
||||
return REQUESTED_STREAM_STRING;
|
||||
case SEARCHED:
|
||||
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.";
|
||||
private String getUserActionString(UserAction userAction) {
|
||||
if(userAction == null) {
|
||||
return "Your description is in another castle.";
|
||||
} else {
|
||||
return userAction.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,28 +427,28 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
return new ErrorInfo[size];
|
||||
}
|
||||
};
|
||||
public int userAction;
|
||||
public String request;
|
||||
public String serviceName;
|
||||
public int message;
|
||||
final public UserAction userAction;
|
||||
final public String request;
|
||||
final public String serviceName;
|
||||
@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) {
|
||||
this.userAction = in.readInt();
|
||||
this.userAction = UserAction.valueOf(in.readString());
|
||||
this.request = in.readString();
|
||||
this.serviceName = in.readString();
|
||||
this.message = in.readInt();
|
||||
}
|
||||
|
||||
public static ErrorInfo make(int userAction, String serviceName, String request, int message) {
|
||||
ErrorInfo info = new ErrorInfo();
|
||||
info.userAction = userAction;
|
||||
info.serviceName = serviceName;
|
||||
info.request = request;
|
||||
info.message = message;
|
||||
return info;
|
||||
public static ErrorInfo make(UserAction userAction, String serviceName, String request, @StringRes int message) {
|
||||
return new ErrorInfo(userAction, serviceName, request, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -475,14 +458,14 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(this.userAction);
|
||||
dest.writeString(this.userAction.name());
|
||||
dest.writeString(this.request);
|
||||
dest.writeString(this.serviceName);
|
||||
dest.writeInt(this.message);
|
||||
}
|
||||
}
|
||||
|
||||
private class IpRagneRequester implements Runnable {
|
||||
private class IpRangeRequester implements Runnable {
|
||||
Handler h = new Handler();
|
||||
public void run() {
|
||||
String ipRange = "none";
|
||||
@ -493,17 +476,16 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip)
|
||||
+ "0.0";
|
||||
} catch(Throwable e) {
|
||||
Log.d(TAG, "Error while error: could not get iprange");
|
||||
e.printStackTrace();
|
||||
Log.w(TAG, "Error while error: could not get iprange", e);
|
||||
} finally {
|
||||
h.post(new IpRageReturnRunnable(ipRange));
|
||||
h.post(new IpRangeReturnRunnable(ipRange));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IpRageReturnRunnable implements Runnable {
|
||||
private class IpRangeReturnRunnable implements Runnable {
|
||||
String ipRange;
|
||||
public IpRageReturnRunnable(String ipRange) {
|
||||
public IpRangeReturnRunnable(String ipRange) {
|
||||
this.ipRange = ipRange;
|
||||
}
|
||||
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) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(R.string.download_path_key);
|
||||
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MOVIES);
|
||||
|
||||
return downloadPath;
|
||||
return prefs.getString(key, Environment.DIRECTORY_MOVIES);
|
||||
}
|
||||
|
||||
public static File getAudioDownloadFolder(Context context) {
|
||||
@ -84,9 +82,7 @@ public class NewPipeSettings {
|
||||
public static String getAudioDownloadPath(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = context.getString(R.string.download_path_audio_key);
|
||||
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MUSIC);
|
||||
|
||||
return downloadPath;
|
||||
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
|
||||
}
|
||||
|
||||
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 static org.schabi.newpipe.report.UserAction.REQUESTED_CHANNEL;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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() {
|
||||
@Override
|
||||
@ -93,7 +95,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||
}
|
||||
});
|
||||
} 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -101,7 +103,7 @@ public class ChannelExtractorWorker extends ExtractorWorker {
|
||||
}
|
||||
});
|
||||
} 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -6,6 +6,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
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.
|
||||
* (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>";
|
||||
switch (errorUserAction) {
|
||||
case ErrorActivity.REQUESTED_STREAM:
|
||||
errorString= ErrorActivity.REQUESTED_STREAM_STRING;
|
||||
case REQUESTED_STREAM:
|
||||
errorString= errorUserAction.getMessage();
|
||||
break;
|
||||
case ErrorActivity.REQUESTED_CHANNEL:
|
||||
errorString= ErrorActivity.REQUESTED_CHANNEL_STRING;
|
||||
case REQUESTED_CHANNEL:
|
||||
errorString= errorUserAction.getMessage();
|
||||
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.SearchResult;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import static org.schabi.newpipe.report.UserAction.*;
|
||||
|
||||
/**
|
||||
* Return list of results based on a query
|
||||
*
|
||||
@ -106,7 +109,7 @@ public class SearchWorker extends AbstractWorker {
|
||||
});
|
||||
} else if (exception instanceof ExtractionException) {
|
||||
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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -115,7 +118,7 @@ public class SearchWorker extends AbstractWorker {
|
||||
});
|
||||
} else {
|
||||
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() {
|
||||
@Override
|
||||
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.StreamInfo;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
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
|
||||
*
|
||||
@ -66,7 +69,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||
StreamExtractor streamExtractor = getService().getExtractorInstance(url);
|
||||
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() {
|
||||
@Override
|
||||
@ -121,7 +124,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||
});
|
||||
} else if (exception instanceof YoutubeStreamExtractor.DecryptException) {
|
||||
// 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -131,9 +134,9 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||
} else if (exception instanceof StreamInfo.StreamExctractException) {
|
||||
if (!streamInfo.errors.isEmpty()) {
|
||||
// !!! 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 {
|
||||
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() {
|
||||
@ -143,7 +146,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||
}
|
||||
});
|
||||
} 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -151,7 +154,7 @@ public class StreamExtractorWorker extends ExtractorWorker {
|
||||
}
|
||||
});
|
||||
} 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() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -11,10 +11,13 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.report.UserAction.*;
|
||||
|
||||
/**
|
||||
* Worker that get suggestions based on the query
|
||||
*
|
||||
@ -79,7 +82,7 @@ public class SuggestionWorker extends AbstractWorker {
|
||||
|
||||
if (exception instanceof ExtractionException) {
|
||||
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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -95,7 +98,7 @@ public class SuggestionWorker extends AbstractWorker {
|
||||
});
|
||||
} else {
|
||||
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() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -9,12 +9,14 @@ public interface DownloadDataSource {
|
||||
|
||||
/**
|
||||
* Load all missions
|
||||
*
|
||||
* @return a list of download missions
|
||||
*/
|
||||
List<DownloadMission> loadMissions();
|
||||
|
||||
/**
|
||||
* Add a downlaod mission to the storage
|
||||
* Add a download mission to the storage
|
||||
*
|
||||
* @param downloadMission the download mission to add
|
||||
* @return the identifier of the mission
|
||||
*/
|
||||
@ -22,6 +24,7 @@ public interface DownloadDataSource {
|
||||
|
||||
/**
|
||||
* Update a download mission which exists in the storage
|
||||
*
|
||||
* @param downloadMission the download mission to update
|
||||
* @throws IllegalArgumentException if the mission was not added to storage
|
||||
*/
|
||||
@ -30,6 +33,7 @@ public interface DownloadDataSource {
|
||||
|
||||
/**
|
||||
* Delete a download mission
|
||||
*
|
||||
* @param downloadMission the mission to delete
|
||||
*/
|
||||
void deleteMission(DownloadMission downloadMission);
|
||||
|
@ -1,48 +1,53 @@
|
||||
package us.shandian.giga.get;
|
||||
|
||||
public interface DownloadManager
|
||||
{
|
||||
int BLOCK_SIZE = 512 * 1024;
|
||||
public interface DownloadManager {
|
||||
int BLOCK_SIZE = 512 * 1024;
|
||||
|
||||
/**
|
||||
* Start a new download mission
|
||||
* @param url the url to download
|
||||
* @param location the location
|
||||
* @param name the name of the file to create
|
||||
* @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.
|
||||
/**
|
||||
* Start a new download mission
|
||||
*
|
||||
* @param url the url to download
|
||||
* @param location the location
|
||||
* @param name the name of the file to create
|
||||
* @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.
|
||||
* @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.
|
||||
/**
|
||||
* Resume the execution of a download mission.
|
||||
*
|
||||
* @param id the identifier of the mission to resume.
|
||||
*/
|
||||
void pauseMission(int id);
|
||||
void resumeMission(int id);
|
||||
|
||||
/**
|
||||
* Deletes the mission from the downloaded list but keeps the downloaded file.
|
||||
* @param id The mission identifier
|
||||
/**
|
||||
* Pause the execution of a download mission.
|
||||
*
|
||||
* @param id the identifier of the mission to pause.
|
||||
*/
|
||||
void deleteMission(int id);
|
||||
void pauseMission(int id);
|
||||
|
||||
/**
|
||||
* Get the download mission by its identifier
|
||||
* @param id the identifier of the download mission
|
||||
* @return the download mission or null if the mission doesn't exist
|
||||
/**
|
||||
* Deletes the mission from the downloaded list but keeps the downloaded file.
|
||||
*
|
||||
* @param id The mission identifier
|
||||
*/
|
||||
DownloadMission getMission(int id);
|
||||
void deleteMission(int id);
|
||||
|
||||
/**
|
||||
* Get the number of download missions.
|
||||
* @return the number of download missions.
|
||||
/**
|
||||
* Get the download mission by its identifier
|
||||
*
|
||||
* @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 us.shandian.giga.util.Utility;
|
||||
|
||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
|
||||
public class DownloadManagerImpl implements DownloadManager
|
||||
{
|
||||
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
|
||||
private final DownloadDataSource mDownloadDataSource;
|
||||
public class DownloadManagerImpl implements DownloadManager {
|
||||
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
|
||||
private final DownloadDataSource mDownloadDataSource;
|
||||
|
||||
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
||||
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
|
||||
mDownloadDataSource = downloadDataSource;
|
||||
loadMissions(searchLocations);
|
||||
}
|
||||
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
|
||||
mDownloadDataSource = downloadDataSource;
|
||||
loadMissions(searchLocations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
|
||||
DownloadMission existingMission = getMissionByLocation(location, name);
|
||||
if(existingMission != null) {
|
||||
// Already downloaded or downloading
|
||||
if(existingMission.finished) {
|
||||
// Overwrite mission
|
||||
@Override
|
||||
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
|
||||
DownloadMission existingMission = getMissionByLocation(location, name);
|
||||
if (existingMission != null) {
|
||||
// Already downloaded or downloading
|
||||
if (existingMission.finished) {
|
||||
// Overwrite mission
|
||||
deleteMission(mMissions.indexOf(existingMission));
|
||||
} else {
|
||||
// Rename file (?)
|
||||
} else {
|
||||
// Rename file (?)
|
||||
try {
|
||||
name = generateUniqueName(location, name);
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to generate unique name", e);
|
||||
name = System.currentTimeMillis() + name ;
|
||||
name = System.currentTimeMillis() + name;
|
||||
Log.i(TAG, "Using " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DownloadMission mission = new DownloadMission(name, url, location);
|
||||
mission.timestamp = System.currentTimeMillis();
|
||||
mission.threadCount = threads;
|
||||
mission.addListener(new MissionListener(mission));
|
||||
new Initializer(mission).start();
|
||||
return insertMission(mission);
|
||||
}
|
||||
DownloadMission mission = new DownloadMission(name, url, location);
|
||||
mission.timestamp = System.currentTimeMillis();
|
||||
mission.threadCount = threads;
|
||||
mission.addListener(new MissionListener(mission));
|
||||
new Initializer(mission).start();
|
||||
return insertMission(mission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
if (!d.running && d.errCode == -1) {
|
||||
d.start();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void resumeMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
if (!d.running && d.errCode == -1) {
|
||||
d.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
if (d.running) {
|
||||
d.pause();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void pauseMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
if (d.running) {
|
||||
d.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMission(int i) {
|
||||
DownloadMission mission = getMission(i);
|
||||
if(mission.finished) {
|
||||
mDownloadDataSource.deleteMission(mission);
|
||||
}
|
||||
mission.delete();
|
||||
mMissions.remove(i);
|
||||
}
|
||||
@Override
|
||||
public void deleteMission(int i) {
|
||||
DownloadMission mission = getMission(i);
|
||||
if (mission.finished) {
|
||||
mDownloadDataSource.deleteMission(mission);
|
||||
}
|
||||
mission.delete();
|
||||
mMissions.remove(i);
|
||||
}
|
||||
|
||||
private void loadMissions(Iterable<String> searchLocations) {
|
||||
mMissions.clear();
|
||||
loadFinishedMissions();
|
||||
for(String location: searchLocations) {
|
||||
loadMissions(location);
|
||||
}
|
||||
private void loadMissions(Iterable<String> searchLocations) {
|
||||
mMissions.clear();
|
||||
loadFinishedMissions();
|
||||
for (String location : searchLocations) {
|
||||
loadMissions(location);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads finished missions from the data source
|
||||
*/
|
||||
private void loadFinishedMissions() {
|
||||
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
|
||||
if(finishedMissions == null) {
|
||||
/**
|
||||
* Loads finished missions from the data source
|
||||
*/
|
||||
private void loadFinishedMissions() {
|
||||
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
|
||||
if (finishedMissions == null) {
|
||||
finishedMissions = new ArrayList<>();
|
||||
}
|
||||
// Ensure its sorted
|
||||
@ -117,251 +118,255 @@ public class DownloadManagerImpl implements DownloadManager
|
||||
}
|
||||
});
|
||||
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
|
||||
for(DownloadMission mission: finishedMissions) {
|
||||
File downloadedFile = mission.getDownloadedFile();
|
||||
if(!downloadedFile.isFile()) {
|
||||
if(DEBUG) {
|
||||
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
|
||||
}
|
||||
mDownloadDataSource.deleteMission(mission);
|
||||
} else {
|
||||
mission.length = downloadedFile.length();
|
||||
mission.finished = true;
|
||||
mission.running = false;
|
||||
mMissions.add(mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (DownloadMission mission : finishedMissions) {
|
||||
File downloadedFile = mission.getDownloadedFile();
|
||||
if (!downloadedFile.isFile()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
|
||||
}
|
||||
mDownloadDataSource.deleteMission(mission);
|
||||
} else {
|
||||
mission.length = downloadedFile.length();
|
||||
mission.finished = true;
|
||||
mission.running = false;
|
||||
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()) {
|
||||
File[] subs = f.listFiles();
|
||||
if (f.exists() && f.isDirectory()) {
|
||||
File[] subs = f.listFiles();
|
||||
|
||||
if(subs == null) {
|
||||
if (subs == null) {
|
||||
Log.e(TAG, "listFiles() returned null");
|
||||
return;
|
||||
}
|
||||
|
||||
for (File sub : subs) {
|
||||
if (sub.isFile() && sub.getName().endsWith(".giga")) {
|
||||
String str = Utility.readFromFile(sub.getAbsolutePath());
|
||||
if (str != null && !str.trim().equals("")) {
|
||||
for (File sub : subs) {
|
||||
if (sub.isFile() && sub.getName().endsWith(".giga")) {
|
||||
String str = Utility.readFromFile(sub.getAbsolutePath());
|
||||
if (str != null && !str.trim().equals("")) {
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "loading mission " + sub.getName());
|
||||
Log.d(TAG, str);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "loading mission " + sub.getName());
|
||||
Log.d(TAG, str);
|
||||
}
|
||||
|
||||
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
|
||||
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
|
||||
|
||||
if (mis.finished) {
|
||||
if(!sub.delete()) {
|
||||
if (mis.finished) {
|
||||
if (!sub.delete()) {
|
||||
Log.w(TAG, "Unable to delete .giga file: " + sub.getPath());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
mis.running = false;
|
||||
mis.recovered = true;
|
||||
insertMission(mis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadMission getMission(int i) {
|
||||
return mMissions.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mMissions.size();
|
||||
}
|
||||
|
||||
private int insertMission(DownloadMission mission) {
|
||||
int i = -1;
|
||||
|
||||
DownloadMission m = null;
|
||||
|
||||
if (mMissions.size() > 0) {
|
||||
do {
|
||||
m = mMissions.get(++i);
|
||||
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
|
||||
|
||||
//if (i > 0) i--;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
mMissions.add(i, mission);
|
||||
|
||||
return i;
|
||||
}
|
||||
mis.running = false;
|
||||
mis.recovered = true;
|
||||
insertMission(mis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadMission getMission(int i) {
|
||||
return mMissions.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mMissions.size();
|
||||
}
|
||||
|
||||
private int insertMission(DownloadMission mission) {
|
||||
int i = -1;
|
||||
|
||||
DownloadMission m = null;
|
||||
|
||||
if (mMissions.size() > 0) {
|
||||
do {
|
||||
m = mMissions.get(++i);
|
||||
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
|
||||
|
||||
//if (i > 0) i--;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
mMissions.add(i, mission);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mission by its location and name
|
||||
* @param location the location
|
||||
* @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
|
||||
* Get a mission by its location and name
|
||||
*
|
||||
* @param location the location
|
||||
* @param name the name
|
||||
* @return the mission or null if no such mission exists
|
||||
*/
|
||||
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)};
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique file name.
|
||||
*
|
||||
* 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
|
||||
/**
|
||||
* Splits the filename into name and extension
|
||||
* <p>
|
||||
* 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) {
|
||||
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
|
||||
* @throws IllegalArgumentException if the location is not a directory
|
||||
* @throws SecurityException if the location is not readable
|
||||
* @throws IllegalArgumentException if the location is not a directory
|
||||
* @throws SecurityException if the location is not readable
|
||||
*/
|
||||
private static String generateUniqueName(String location, String name) {
|
||||
if(location == null) throw new NullPointerException("location is null");
|
||||
if(name == null) throw new NullPointerException("name is null");
|
||||
File destination = new File(location);
|
||||
if(!destination.isDirectory()) {
|
||||
throw new IllegalArgumentException("location is not a directory: " + location);
|
||||
}
|
||||
final String[] nameParts = splitName(name);
|
||||
String[] existingName = destination.list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.startsWith(nameParts[0]);
|
||||
}
|
||||
});
|
||||
Arrays.sort(existingName);
|
||||
String newName;
|
||||
int downloadIndex = 0;
|
||||
do {
|
||||
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
|
||||
++downloadIndex;
|
||||
if(downloadIndex == 1000) { // Probably an error on our side
|
||||
throw new RuntimeException("Too many existing files");
|
||||
}
|
||||
} while (Arrays.binarySearch(existingName, newName) >= 0);
|
||||
return newName;
|
||||
}
|
||||
private static String generateUniqueName(String location, String name) {
|
||||
if (location == null) throw new NullPointerException("location is null");
|
||||
if (name == null) throw new NullPointerException("name is null");
|
||||
File destination = new File(location);
|
||||
if (!destination.isDirectory()) {
|
||||
throw new IllegalArgumentException("location is not a directory: " + location);
|
||||
}
|
||||
final String[] nameParts = splitName(name);
|
||||
String[] existingName = destination.list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.startsWith(nameParts[0]);
|
||||
}
|
||||
});
|
||||
Arrays.sort(existingName);
|
||||
String newName;
|
||||
int downloadIndex = 0;
|
||||
do {
|
||||
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
|
||||
++downloadIndex;
|
||||
if (downloadIndex == 1000) { // Probably an error on our side
|
||||
throw new RuntimeException("Too many existing files");
|
||||
}
|
||||
} while (Arrays.binarySearch(existingName, newName) >= 0);
|
||||
return newName;
|
||||
}
|
||||
|
||||
private class Initializer extends Thread {
|
||||
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++;
|
||||
}
|
||||
|
||||
private class Initializer extends Thread {
|
||||
private DownloadMission mission;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
public Initializer(DownloadMission mission) {
|
||||
this.mission = mission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for mission to finish to add it to the {@link #mDownloadDataSource}
|
||||
*/
|
||||
private class MissionListener implements DownloadMission.MissionListener {
|
||||
private final DownloadMission mMission;
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
URL url = new URL(mission.url);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
mission.length = conn.getContentLength();
|
||||
|
||||
private MissionListener(DownloadMission mission) {
|
||||
if(mission == null) throw new NullPointerException("mission is null");
|
||||
// Could the mission be passed in onFinish()?
|
||||
mMission = mission;
|
||||
}
|
||||
if (mission.length <= 0) {
|
||||
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||
}
|
||||
// Open again
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
|
||||
|
||||
@Override
|
||||
public void onFinish(DownloadMission downloadMission) {
|
||||
mDownloadDataSource.addMission(mMission);
|
||||
}
|
||||
if (conn.getResponseCode() != 206) {
|
||||
// Fallback to single thread if no partial content support
|
||||
mission.fallback = true;
|
||||
|
||||
@Override
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
}
|
||||
}
|
||||
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();
|
||||
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;
|
||||
|
||||
public class DownloadMission
|
||||
{
|
||||
private static final String TAG = DownloadMission.class.getSimpleName();
|
||||
public class DownloadMission {
|
||||
private static final String TAG = DownloadMission.class.getSimpleName();
|
||||
|
||||
public interface MissionListener {
|
||||
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;
|
||||
public interface MissionListener {
|
||||
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The filename
|
||||
*/
|
||||
public String name;
|
||||
void onProgressUpdate(DownloadMission downloadMission, long done, long total);
|
||||
|
||||
/**
|
||||
* The url of the file to download
|
||||
*/
|
||||
public String url;
|
||||
void onFinish(DownloadMission downloadMission);
|
||||
|
||||
/**
|
||||
* The directory to store the download
|
||||
*/
|
||||
public String location;
|
||||
void onError(DownloadMission downloadMission, int errCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
|
||||
*/
|
||||
public long blocks;
|
||||
public static final int ERROR_SERVER_UNSUPPORTED = 206;
|
||||
public static final int ERROR_UNKNOWN = 233;
|
||||
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* The filename
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
public String name;
|
||||
|
||||
/**
|
||||
* Set the download position of the file
|
||||
* @param threadId the identifier of the thread
|
||||
* @param position the download position of the thread
|
||||
/**
|
||||
* The url of the file to download
|
||||
*/
|
||||
public void setPosition(int threadId, long position) {
|
||||
threadPositions.set(threadId, position);
|
||||
}
|
||||
public String url;
|
||||
|
||||
/**
|
||||
* Get the position of a thread
|
||||
* @param threadId the identifier of the thread
|
||||
* @return the position for the thread
|
||||
/**
|
||||
* The directory to store the download
|
||||
*/
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public String location;
|
||||
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
|
||||
*/
|
||||
private String getMetaFilename() {
|
||||
return location + "/" + name + ".giga";
|
||||
}
|
||||
public long blocks;
|
||||
|
||||
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,
|
||||
* an error occurs or the process is stopped.
|
||||
*/
|
||||
public class DownloadRunnable implements Runnable
|
||||
{
|
||||
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
||||
|
||||
private final DownloadMission mMission;
|
||||
private final int mId;
|
||||
|
||||
public DownloadRunnable(DownloadMission mission, int id) {
|
||||
if(mission == null) throw new NullPointerException("mission is null");
|
||||
mMission = mission;
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean retry = mMission.recovered;
|
||||
long position = mMission.getPosition(mId);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":default pos " + position);
|
||||
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
||||
}
|
||||
|
||||
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
mMission.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG && retry) {
|
||||
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
|
||||
}
|
||||
|
||||
// Wait for an unblocked position
|
||||
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":position " + position + " preserved, passing");
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
retry = false;
|
||||
|
||||
if (position >= mMission.blocks) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":preserving position " + position);
|
||||
}
|
||||
|
||||
mMission.preserveBlock(position);
|
||||
mMission.setPosition(mId, position);
|
||||
|
||||
long start = position * DownloadManager.BLOCK_SIZE;
|
||||
long end = start + DownloadManager.BLOCK_SIZE - 1;
|
||||
|
||||
if (end >= mMission.length) {
|
||||
end = mMission.length - 1;
|
||||
}
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
int total = 0;
|
||||
|
||||
try {
|
||||
URL url = new URL(mMission.url);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
|
||||
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
||||
}
|
||||
|
||||
// A server may be ignoring the range request
|
||||
if (conn.getResponseCode() != 206) {
|
||||
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
||||
f.seek(start);
|
||||
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
||||
byte[] buf = new byte[512];
|
||||
|
||||
while (start < end && mMission.running) {
|
||||
int len = ipt.read(buf, 0, 512);
|
||||
|
||||
if (len == -1) {
|
||||
break;
|
||||
} else {
|
||||
start += len;
|
||||
total += len;
|
||||
f.write(buf, 0, len);
|
||||
notifyProgress(len);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG && mMission.running) {
|
||||
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
|
||||
}
|
||||
|
||||
f.close();
|
||||
ipt.close();
|
||||
|
||||
// TODO We should save progress for each thread
|
||||
} catch (Exception e) {
|
||||
// TODO Retry count limit & notify error
|
||||
retry = true;
|
||||
|
||||
notifyProgress(-total);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "thread " + mId + " exited main loop");
|
||||
}
|
||||
|
||||
if (mMission.errCode == -1 && mMission.running) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "no error has happened, notifying");
|
||||
}
|
||||
notifyFinished();
|
||||
}
|
||||
|
||||
if (DEBUG && !mMission.running) {
|
||||
Log.d(TAG, "The mission has been paused. Passing.");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
public class DownloadRunnable implements Runnable {
|
||||
private static final String TAG = DownloadRunnable.class.getSimpleName();
|
||||
|
||||
private final DownloadMission mMission;
|
||||
private final int mId;
|
||||
|
||||
public DownloadRunnable(DownloadMission mission, int id) {
|
||||
if (mission == null) throw new NullPointerException("mission is null");
|
||||
mMission = mission;
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean retry = mMission.recovered;
|
||||
long position = mMission.getPosition(mId);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":default pos " + position);
|
||||
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
|
||||
}
|
||||
|
||||
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
mMission.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG && retry) {
|
||||
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
|
||||
}
|
||||
|
||||
// Wait for an unblocked position
|
||||
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":position " + position + " preserved, passing");
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
retry = false;
|
||||
|
||||
if (position >= mMission.blocks) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":preserving position " + position);
|
||||
}
|
||||
|
||||
mMission.preserveBlock(position);
|
||||
mMission.setPosition(mId, position);
|
||||
|
||||
long start = position * DownloadManager.BLOCK_SIZE;
|
||||
long end = start + DownloadManager.BLOCK_SIZE - 1;
|
||||
|
||||
if (end >= mMission.length) {
|
||||
end = mMission.length - 1;
|
||||
}
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
int total = 0;
|
||||
|
||||
try {
|
||||
URL url = new URL(mMission.url);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
|
||||
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
||||
}
|
||||
|
||||
// A server may be ignoring the range request
|
||||
if (conn.getResponseCode() != 206) {
|
||||
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
|
||||
f.seek(start);
|
||||
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
|
||||
byte[] buf = new byte[512];
|
||||
|
||||
while (start < end && mMission.running) {
|
||||
int len = ipt.read(buf, 0, 512);
|
||||
|
||||
if (len == -1) {
|
||||
break;
|
||||
} else {
|
||||
start += len;
|
||||
total += len;
|
||||
f.write(buf, 0, len);
|
||||
notifyProgress(len);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG && mMission.running) {
|
||||
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
|
||||
}
|
||||
|
||||
f.close();
|
||||
ipt.close();
|
||||
|
||||
// TODO We should save progress for each thread
|
||||
} catch (Exception e) {
|
||||
// TODO Retry count limit & notify error
|
||||
retry = true;
|
||||
|
||||
notifyProgress(-total);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "thread " + mId + " exited main loop");
|
||||
}
|
||||
|
||||
if (mMission.errCode == -1 && mMission.running) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "no error has happened, notifying");
|
||||
}
|
||||
notifyFinished();
|
||||
}
|
||||
|
||||
if (DEBUG && !mMission.running) {
|
||||
Log.d(TAG, "The mission has been paused. Passing.");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,70 +6,69 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
// Single-threaded fallback mode
|
||||
public class DownloadRunnableFallback implements Runnable
|
||||
{
|
||||
private final DownloadMission mMission;
|
||||
//private int mId;
|
||||
|
||||
public DownloadRunnableFallback(DownloadMission mission) {
|
||||
if(mission == null) throw new NullPointerException("mission is null");
|
||||
//mId = id;
|
||||
mMission = mission;
|
||||
}
|
||||
public class DownloadRunnableFallback implements Runnable {
|
||||
private final DownloadMission mMission;
|
||||
//private int mId;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
public DownloadRunnableFallback(DownloadMission mission) {
|
||||
if (mission == null) throw new NullPointerException("mission is null");
|
||||
//mId = id;
|
||||
mMission = mission;
|
||||
}
|
||||
|
||||
private void notifyFinished() {
|
||||
synchronized (mMission) {
|
||||
mMission.notifyFinished();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
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.
|
||||
*
|
||||
* @param downloadMission the download mission
|
||||
* @return the content values
|
||||
*/
|
||||
@ -88,7 +89,7 @@ public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
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;
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME));
|
||||
String location = cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCATION));
|
||||
|
@ -37,7 +37,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||
null, null, null, DownloadMissionSQLiteHelper.KEY_TIMESTAMP);
|
||||
|
||||
int count = cursor.getCount();
|
||||
if(count == 0) return new ArrayList<>();
|
||||
if (count == 0) return new ArrayList<>();
|
||||
result = new ArrayList<>(count);
|
||||
while (cursor.moveToNext()) {
|
||||
result.add(DownloadMissionSQLiteHelper.getMissionFromCursor(cursor));
|
||||
@ -47,7 +47,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||
|
||||
@Override
|
||||
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();
|
||||
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
||||
database.insert(MISSIONS_TABLE_NAME, null, values);
|
||||
@ -55,25 +55,25 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||
|
||||
@Override
|
||||
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();
|
||||
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
||||
String whereClause = KEY_LOCATION+ " = ? AND " +
|
||||
String whereClause = KEY_LOCATION + " = ? AND " +
|
||||
KEY_NAME + " = ?";
|
||||
int rowsAffected = database.update(MISSIONS_TABLE_NAME, values,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
database.delete(MISSIONS_TABLE_NAME,
|
||||
KEY_LOCATION + " = ? AND " +
|
||||
KEY_NAME + " = ?",
|
||||
KEY_NAME + " = ?",
|
||||
new String[]{downloadMission.location, downloadMission.name});
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
@ -16,6 +15,7 @@ import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.support.v4.app.NotificationCompat.Builder;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.PermissionChecker;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
@ -34,237 +34,235 @@ import us.shandian.giga.get.sqlite.SQLiteDownloadDataSource;
|
||||
|
||||
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}.
|
||||
*/
|
||||
private static final int UPDATE_MESSAGE = 0;
|
||||
private static final int NOTIFICATION_ID = 1000;
|
||||
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
|
||||
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_THREADS = "DownloadManagerService.extra.threads";
|
||||
/**
|
||||
* Message code of update messages stored as {@link Message#what}.
|
||||
*/
|
||||
private static final int UPDATE_MESSAGE = 0;
|
||||
private static final int NOTIFICATION_ID = 1000;
|
||||
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
|
||||
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_THREADS = "DownloadManagerService.extra.threads";
|
||||
|
||||
|
||||
private DMBinder mBinder;
|
||||
private DownloadManager mManager;
|
||||
private Notification mNotification;
|
||||
private Handler mHandler;
|
||||
private long mLastTimeStamp = System.currentTimeMillis();
|
||||
private DownloadDataSource mDataSource;
|
||||
private DMBinder mBinder;
|
||||
private DownloadManager mManager;
|
||||
private Notification mNotification;
|
||||
private Handler mHandler;
|
||||
private long mLastTimeStamp = System.currentTimeMillis();
|
||||
private DownloadDataSource mDataSource;
|
||||
|
||||
|
||||
|
||||
private MissionListener missionListener = new MissionListener();
|
||||
private MissionListener missionListener = new MissionListener();
|
||||
|
||||
|
||||
private void notifyMediaScanner(DownloadMission mission) {
|
||||
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
|
||||
// notify media scanner on downloaded media file ...
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
||||
}
|
||||
private void notifyMediaScanner(DownloadMission mission) {
|
||||
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
|
||||
// notify media scanner on downloaded media file ...
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreate");
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreate");
|
||||
}
|
||||
|
||||
mBinder = new DMBinder();
|
||||
if(mDataSource == null) {
|
||||
mDataSource = new SQLiteDownloadDataSource(this);
|
||||
}
|
||||
if (mManager == null) {
|
||||
ArrayList<String> paths = new ArrayList<>(2);
|
||||
paths.add(NewPipeSettings.getVideoDownloadPath(this));
|
||||
paths.add(NewPipeSettings.getAudioDownloadPath(this));
|
||||
mManager = new DownloadManagerImpl(paths, mDataSource);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "mManager == null");
|
||||
Log.d(TAG, "Download directory: " + paths);
|
||||
}
|
||||
}
|
||||
mBinder = new DMBinder();
|
||||
if (mDataSource == null) {
|
||||
mDataSource = new SQLiteDownloadDataSource(this);
|
||||
}
|
||||
if (mManager == null) {
|
||||
ArrayList<String> paths = new ArrayList<>(2);
|
||||
paths.add(NewPipeSettings.getVideoDownloadPath(this));
|
||||
paths.add(NewPipeSettings.getAudioDownloadPath(this));
|
||||
mManager = new DownloadManagerImpl(paths, mDataSource);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "mManager == null");
|
||||
Log.d(TAG, "Download directory: " + paths);
|
||||
}
|
||||
}
|
||||
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_MAIN);
|
||||
i.setClass(this, DownloadActivity.class);
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_MAIN);
|
||||
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)
|
||||
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
||||
.setContentTitle(getString(R.string.msg_running))
|
||||
.setContentText(getString(R.string.msg_running_detail));
|
||||
Builder builder = new Builder(this)
|
||||
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
||||
.setContentTitle(getString(R.string.msg_running))
|
||||
.setContentText(getString(R.string.msg_running_detail));
|
||||
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
new Intent(this, DownloadActivity.class)
|
||||
.setAction(DownloadActivity.INTENT_LIST),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
new Intent(this, DownloadActivity.class)
|
||||
.setAction(DownloadActivity.INTENT_LIST),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
builder.setContentIntent(pendingIntent);
|
||||
builder.setContentIntent(pendingIntent);
|
||||
|
||||
mNotification = builder.build();
|
||||
mNotification = builder.build();
|
||||
|
||||
HandlerThread thread = new HandlerThread("ServiceMessenger");
|
||||
thread.start();
|
||||
|
||||
mHandler = new Handler(thread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case UPDATE_MESSAGE: {
|
||||
int runningCount = 0;
|
||||
HandlerThread thread = new HandlerThread("ServiceMessenger");
|
||||
thread.start();
|
||||
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
if (mManager.getMission(i).running) {
|
||||
runningCount++;
|
||||
}
|
||||
}
|
||||
updateState(runningCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
mHandler = new Handler(thread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case UPDATE_MESSAGE: {
|
||||
int runningCount = 0;
|
||||
|
||||
private void startMissionAsync(final String url, final String location, final String name,
|
||||
final boolean isAudio, final int threads) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int missionId = mManager.startMission(url, location, name, isAudio, threads);
|
||||
mBinder.onMissionAdded(mManager.getMission(missionId));
|
||||
}
|
||||
});
|
||||
}
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
if (mManager.getMission(i).running) {
|
||||
runningCount++;
|
||||
}
|
||||
}
|
||||
updateState(runningCount);
|
||||
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
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Destroying");
|
||||
}
|
||||
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
mManager.pauseMission(i);
|
||||
}
|
||||
private void startMissionAsync(final String url, final String location, final String name,
|
||||
final boolean isAudio, final int threads) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int missionId = mManager.startMission(url, location, name, isAudio, threads);
|
||||
mBinder.onMissionAdded(mManager.getMission(missionId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
int permissionCheck;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
@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;
|
||||
}
|
||||
|
||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, "Permission denied (write)", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
return mBinder;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Destroying");
|
||||
}
|
||||
|
||||
private void postUpdateMessage() {
|
||||
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
|
||||
}
|
||||
|
||||
private void updateState(int runningCount) {
|
||||
if (runningCount == 0) {
|
||||
stopForeground(true);
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, mNotification);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
mManager.pauseMission(i);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
int permissionCheck;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
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 {
|
||||
@Override
|
||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - mLastTimeStamp;
|
||||
if (delta > 2000) {
|
||||
postUpdateMessage();
|
||||
mLastTimeStamp = now;
|
||||
}
|
||||
}
|
||||
private class MissionListener implements DownloadMission.MissionListener {
|
||||
@Override
|
||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - mLastTimeStamp;
|
||||
if (delta > 2000) {
|
||||
postUpdateMessage();
|
||||
mLastTimeStamp = now;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(DownloadMission downloadMission) {
|
||||
postUpdateMessage();
|
||||
notifyMediaScanner(downloadMission);
|
||||
}
|
||||
@Override
|
||||
public void onFinish(DownloadMission downloadMission) {
|
||||
postUpdateMessage();
|
||||
notifyMediaScanner(downloadMission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
postUpdateMessage();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
postUpdateMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wrapper of DownloadManager
|
||||
public class DMBinder extends Binder {
|
||||
public DownloadManager getDownloadManager() {
|
||||
return mManager;
|
||||
}
|
||||
|
||||
public void onMissionAdded(DownloadMission mission) {
|
||||
mission.addListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
public void onMissionRemoved(DownloadMission mission) {
|
||||
mission.removeListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
}
|
||||
// Wrapper of DownloadManager
|
||||
public class DMBinder extends Binder {
|
||||
public DownloadManager getDownloadManager() {
|
||||
return mManager;
|
||||
}
|
||||
|
||||
public void onMissionAdded(DownloadMission mission) {
|
||||
mission.addListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
public void onMissionRemoved(DownloadMission mission) {
|
||||
mission.removeListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@ -18,14 +20,13 @@ import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import us.shandian.giga.get.DownloadManager;
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
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_READ_URI_PERMISSION;
|
||||
|
||||
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder>
|
||||
{
|
||||
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
|
||||
private static final String TAG = "MissionAdapter";
|
||||
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder> {
|
||||
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
|
||||
private static final String TAG = "MissionAdapter";
|
||||
|
||||
static {
|
||||
ALGORITHMS.put(R.id.md5, "MD5");
|
||||
ALGORITHMS.put(R.id.sha1, "SHA1");
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private LayoutInflater mInflater;
|
||||
private DownloadManager mManager;
|
||||
private DownloadManagerService.DMBinder mBinder;
|
||||
private int mLayout;
|
||||
|
||||
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
|
||||
mContext = context;
|
||||
mManager = manager;
|
||||
mBinder = binder;
|
||||
|
||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
|
||||
}
|
||||
static {
|
||||
ALGORITHMS.put(R.id.md5, "MD5");
|
||||
ALGORITHMS.put(R.id.sha1, "SHA1");
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private LayoutInflater mInflater;
|
||||
private DownloadManager mManager;
|
||||
private DownloadManagerService.DMBinder mBinder;
|
||||
private int mLayout;
|
||||
|
||||
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
|
||||
mContext = context;
|
||||
mManager = manager;
|
||||
mBinder = binder;
|
||||
|
||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
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() {
|
||||
@Override
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showDetail(h);
|
||||
}
|
||||
});*/
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
|
||||
DownloadMission ms = mManager.getMission(pos);
|
||||
h.mission = ms;
|
||||
h.position = pos;
|
||||
|
||||
Utility.FileType type = Utility.getFileType(ms.name);
|
||||
|
||||
h.icon.setImageResource(Utility.getIconForFileType(type));
|
||||
h.name.setText(ms.name);
|
||||
h.size.setText(Utility.formatBytes(ms.length));
|
||||
|
||||
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
|
||||
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
|
||||
public int getItemCount() {
|
||||
return mManager.getCount();
|
||||
}
|
||||
@Override
|
||||
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
|
||||
DownloadMission ms = mManager.getMission(pos);
|
||||
h.mission = ms;
|
||||
h.position = pos;
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Utility.FileType type = Utility.getFileType(ms.name);
|
||||
|
||||
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);
|
||||
h.icon.setImageResource(Utility.getIconForFileType(type));
|
||||
h.name.setText(ms.name);
|
||||
h.size.setText(Utility.formatBytes(ms.length));
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
h.observer = new MissionObserver(this, h);
|
||||
ms.addListener(h.observer);
|
||||
|
||||
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);
|
||||
}
|
||||
updateProgress(h);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private class ChecksumTask extends AsyncTask<String, Void, String> {
|
||||
ProgressDialog prog;
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mManager.getCount();
|
||||
}
|
||||
|
||||
@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
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
return Utility.checksum(params[0], params[1]);
|
||||
}
|
||||
private void updateProgress(ViewHolder h) {
|
||||
updateProgress(h, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
prog.dismiss();
|
||||
Utility.copyToClipboard(mContext, result);
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public DownloadMission mission;
|
||||
public int position;
|
||||
private void updateProgress(ViewHolder h, boolean finished) {
|
||||
if (h.mission == null) return;
|
||||
|
||||
public TextView status;
|
||||
public ImageView icon;
|
||||
public TextView name;
|
||||
public TextView size;
|
||||
public View bkg;
|
||||
public ImageView menu;
|
||||
public ProgressDrawable progress;
|
||||
public MissionObserver observer;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
public long lastTimeStamp = -1;
|
||||
public long lastDone = -1;
|
||||
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);
|
||||
}
|
||||
if (h.lastTimeStamp == -1) {
|
||||
h.lastTimeStamp = now;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
if (h.lastDone == -1) {
|
||||
h.lastDone = h.mission.done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
mAdapter.updateProgress(mHolder);
|
||||
}
|
||||
|
||||
}
|
||||
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) {
|
||||
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.PixelFormat;
|
||||
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
|
||||
{
|
||||
private float mProgress;
|
||||
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();
|
||||
}
|
||||
public class ProgressDrawable extends Drawable {
|
||||
private float mProgress;
|
||||
private int mBackgroundColor, mForegroundColor;
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
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);
|
||||
}
|
||||
public ProgressDrawable(Context context, @ColorRes int background, @ColorRes int foreground) {
|
||||
this(ContextCompat.getColor(context, background), ContextCompat.getColor(context, foreground));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
// Unsupported
|
||||
}
|
||||
public ProgressDrawable(int background, int foreground) {
|
||||
mBackgroundColor = background;
|
||||
mForegroundColor = foreground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter filter) {
|
||||
// Unsupported
|
||||
}
|
||||
public void setProgress(float progress) {
|
||||
mProgress = progress;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
}
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
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;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import us.shandian.giga.util.Utility;
|
||||
|
||||
public abstract class ToolbarActivity extends ActionBarActivity
|
||||
{
|
||||
protected Toolbar mToolbar;
|
||||
public abstract class ToolbarActivity extends ActionBarActivity {
|
||||
protected Toolbar mToolbar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getLayoutResource());
|
||||
|
||||
mToolbar = Utility.findViewById(this, R.id.toolbar);
|
||||
|
||||
setSupportActionBar(mToolbar);
|
||||
}
|
||||
|
||||
protected abstract int getLayoutResource();
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getLayoutResource());
|
||||
|
||||
mToolbar = (Toolbar) this.findViewById(R.id.toolbar);
|
||||
|
||||
setSupportActionBar(mToolbar);
|
||||
}
|
||||
|
||||
protected abstract int getLayoutResource();
|
||||
}
|
||||
|
@ -3,11 +3,10 @@ package us.shandian.giga.ui.fragment;
|
||||
import us.shandian.giga.get.DownloadManager;
|
||||
import us.shandian.giga.service.DownloadManagerService;
|
||||
|
||||
public class AllMissionsFragment extends MissionsFragment
|
||||
{
|
||||
public class AllMissionsFragment extends MissionsFragment {
|
||||
|
||||
@Override
|
||||
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
|
||||
return binder.getDownloadManager();
|
||||
}
|
||||
@Override
|
||||
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
|
||||
return binder.getDownloadManager();
|
||||
}
|
||||
}
|
||||
|
@ -23,126 +23,128 @@ import org.schabi.newpipe.R;
|
||||
import us.shandian.giga.get.DownloadManager;
|
||||
import us.shandian.giga.service.DownloadManagerService;
|
||||
import us.shandian.giga.ui.adapter.MissionAdapter;
|
||||
import us.shandian.giga.util.Utility;
|
||||
|
||||
public abstract class MissionsFragment extends Fragment
|
||||
{
|
||||
private DownloadManager mManager;
|
||||
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() {
|
||||
public abstract class MissionsFragment extends Fragment {
|
||||
private DownloadManager mManager;
|
||||
private DownloadManagerService.DMBinder mBinder;
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
mBinder = (DownloadManagerService.DMBinder) binder;
|
||||
mManager = setupDownloadManager(mBinder);
|
||||
updateList();
|
||||
}
|
||||
private SharedPreferences mPrefs;
|
||||
private boolean mLinear;
|
||||
private MenuItem mSwitch;
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
// What to do?
|
||||
}
|
||||
private RecyclerView mList;
|
||||
private MissionAdapter mAdapter;
|
||||
private GridLayoutManager mGridManager;
|
||||
private LinearLayoutManager mLinearManager;
|
||||
private Context mActivity;
|
||||
|
||||
|
||||
};
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.missions, container, false);
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
mBinder = (DownloadManagerService.DMBinder) binder;
|
||||
mManager = setupDownloadManager(mBinder);
|
||||
updateList();
|
||||
}
|
||||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
mLinear = mPrefs.getBoolean("linear", false);
|
||||
@Override
|
||||
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
|
||||
public void onAttach(Context activity) {
|
||||
super.onAttach(activity);
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.missions, container, false);
|
||||
|
||||
// Bug: in api< 23 this is never called
|
||||
// so mActivity=null
|
||||
// so app crashes with nullpointer exception
|
||||
mActivity = activity;
|
||||
}
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
mLinear = mPrefs.getBoolean("linear", false);
|
||||
|
||||
/** deprecated in API level 23,
|
||||
* but must remain to allow compatibility with api<23 */
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
// Bind the service
|
||||
Intent i = new Intent();
|
||||
i.setClass(getActivity(), DownloadManagerService.class);
|
||||
getActivity().bindService(i, mConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
mActivity = activity;
|
||||
}
|
||||
// Views
|
||||
mList = (RecyclerView) v.findViewById(R.id.mission_recycler);
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
getActivity().unbindService(mConnection);
|
||||
}
|
||||
// Init
|
||||
mGridManager = new GridLayoutManager(getActivity(), 2);
|
||||
mLinearManager = new LinearLayoutManager(getActivity());
|
||||
mList.setLayoutManager(mGridManager);
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
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()) {
|
||||
case R.id.switch_mode:
|
||||
case R.id.switch_mode:
|
||||
mLinear = !mLinear;
|
||||
updateList();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyChange() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void updateList() {
|
||||
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
|
||||
|
||||
if (mLinear) {
|
||||
mList.setLayoutManager(mLinearManager);
|
||||
} else {
|
||||
mList.setLayoutManager(mGridManager);
|
||||
}
|
||||
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
if (mSwitch != null) {
|
||||
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
|
||||
}
|
||||
|
||||
mPrefs.edit().putBoolean("linear", mLinear).commit();
|
||||
}
|
||||
|
||||
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
|
||||
public void notifyChange() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void updateList() {
|
||||
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
|
||||
|
||||
if (mLinear) {
|
||||
mList.setLayoutManager(mLinearManager);
|
||||
} else {
|
||||
mList.setLayoutManager(mGridManager);
|
||||
}
|
||||
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
if (mSwitch != null) {
|
||||
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
|
||||
}
|
||||
|
||||
mPrefs.edit().putBoolean("linear", mLinear).commit();
|
||||
}
|
||||
|
||||
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
|
||||
}
|
||||
|
@ -9,77 +9,76 @@ import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
//todo: replace this by using the internal crash handler of newpipe
|
||||
public class CrashHandler implements Thread.UncaughtExceptionHandler
|
||||
{
|
||||
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_TAG = CRASH_DIR + ".crashed";
|
||||
public class CrashHandler implements Thread.UncaughtExceptionHandler {
|
||||
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_TAG = CRASH_DIR + ".crashed";
|
||||
|
||||
private static String ANDROID = Build.VERSION.RELEASE;
|
||||
private static String MODEL = Build.MODEL;
|
||||
private static String MANUFACTURER = Build.MANUFACTURER;
|
||||
private static String ANDROID = Build.VERSION.RELEASE;
|
||||
private static String MODEL = Build.MODEL;
|
||||
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) {
|
||||
try {
|
||||
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
VERSION = info.versionName + info.versionCode;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public static void init(Context context) {
|
||||
try {
|
||||
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
VERSION = info.versionName + info.versionCode;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
new CrashHandler();
|
||||
}
|
||||
public static void register() {
|
||||
new CrashHandler();
|
||||
}
|
||||
|
||||
private CrashHandler() {
|
||||
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
|
||||
Thread.currentThread().setUncaughtExceptionHandler(this);
|
||||
}
|
||||
private CrashHandler() {
|
||||
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
|
||||
Thread.currentThread().setUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
File f = new File(CRASH_LOG);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
} else {
|
||||
try {
|
||||
new File(CRASH_DIR).mkdirs();
|
||||
f.createNewFile();
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
File f = new File(CRASH_LOG);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
} else {
|
||||
try {
|
||||
new File(CRASH_DIR).mkdirs();
|
||||
f.createNewFile();
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter p;
|
||||
try {
|
||||
p = new PrintWriter(f);
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
PrintWriter p;
|
||||
try {
|
||||
p = new PrintWriter(f);
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.write("Android Version: " + ANDROID + "\n");
|
||||
p.write("Device Model: " + MODEL + "\n");
|
||||
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
|
||||
p.write("App Version: " + VERSION + "\n");
|
||||
p.write("*********************\n");
|
||||
throwable.printStackTrace(p);
|
||||
p.write("Android Version: " + ANDROID + "\n");
|
||||
p.write("Device Model: " + MODEL + "\n");
|
||||
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
|
||||
p.write("App Version: " + VERSION + "\n");
|
||||
p.write("*********************\n");
|
||||
throwable.printStackTrace(p);
|
||||
|
||||
p.close();
|
||||
p.close();
|
||||
|
||||
try {
|
||||
new File(CRASH_TAG).createNewFile();
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
new File(CRASH_TAG).createNewFile();
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPrevious != null) {
|
||||
mPrevious.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
if (mPrevious != null) {
|
||||
mPrevious.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,256 +1,221 @@
|
||||
package us.shandian.giga.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
import org.schabi.newpipe.R;
|
||||
public class Utility {
|
||||
|
||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
|
||||
public enum FileType {
|
||||
VIDEO,
|
||||
MUSIC,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
public class Utility
|
||||
{
|
||||
|
||||
public static enum FileType {
|
||||
VIDEO,
|
||||
MUSIC,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
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 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 FileType getFileType(String file) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
public static void writeToFile(String fileName, String content) {
|
||||
try {
|
||||
writeToFile(fileName, content.getBytes("UTF-8"));
|
||||
} catch (Exception e) {
|
||||
|
||||
public static Boolean isMusicFile(String file)
|
||||
{
|
||||
return Utility.getFileType(file) == FileType.MUSIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Boolean isVideoFile(String file)
|
||||
{
|
||||
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 void writeToFile(String fileName, byte[] content) {
|
||||
File f = new File(fileName);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (!f.exists()) {
|
||||
try {
|
||||
f.createNewFile();
|
||||
} catch (Exception e) {
|
||||
|
||||
public static boolean isDirectoryAvailble(String path) {
|
||||
File dir = new File(path);
|
||||
return dir.exists() && dir.isDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDownloadDirectoryAvailble(Context context) {
|
||||
return isDirectoryAvailble(NewPipeSettings.getVideoDownloadPath(context));
|
||||
}
|
||||
try {
|
||||
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);
|
||||
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
|
||||
i.putExtra(FilePickerActivity.EXTRA_MODE, AbstractFilePickerFragment.MODE_DIR);
|
||||
activity.startActivityForResult(i, 233);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getFileExt(String url) {
|
||||
int index;
|
||||
if ((index = url.indexOf("?")) > -1) {
|
||||
url = url.substring(0, index);
|
||||
}
|
||||
|
||||
index = url.lastIndexOf(".");
|
||||
if (index == -1) {
|
||||
return null;
|
||||
} else {
|
||||
String ext = url.substring(index);
|
||||
if ((index = ext.indexOf("%")) > -1) {
|
||||
ext = ext.substring(0, index);
|
||||
}
|
||||
if ((index = ext.indexOf("/")) > -1) {
|
||||
ext = ext.substring(0, index);
|
||||
}
|
||||
return ext.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static FileType getFileType(String file) {
|
||||
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 {
|
||||
|
||||
private DownloadManagerImpl downloadManager;
|
||||
private DownloadDataSource dowloadDataSource;
|
||||
private DownloadDataSource downloadDataSource;
|
||||
private ArrayList<DownloadMission> missions;
|
||||
|
||||
@org.junit.Before
|
||||
public void setUp() throws Exception {
|
||||
dowloadDataSource = mock(DownloadDataSource.class);
|
||||
downloadDataSource = mock(DownloadDataSource.class);
|
||||
missions = new ArrayList<>();
|
||||
for(int i = 0; i < 50; ++i){
|
||||
missions.add(generateFinishedDownloadMission());
|
||||
}
|
||||
when(dowloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), dowloadDataSource);
|
||||
when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), downloadDataSource);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
@ -82,10 +82,10 @@ public class DownloadManagerImplTest {
|
||||
missions.add(mission);
|
||||
}
|
||||
|
||||
dowloadDataSource = mock(DownloadDataSource.class);
|
||||
when(dowloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), dowloadDataSource);
|
||||
verify(dowloadDataSource, times(1)).loadMissions();
|
||||
downloadDataSource = mock(DownloadDataSource.class);
|
||||
when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions));
|
||||
downloadManager = new DownloadManagerImpl(new ArrayList<String>(), downloadDataSource);
|
||||
verify(downloadDataSource, times(1)).loadMissions();
|
||||
|
||||
assertEquals(50, downloadManager.getCount());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user