Merge pull request #1623 from TeamNewPipe/refactor_and_bugfix

Refactor and bugfix
This commit is contained in:
Christian Schabesberger 2018-08-28 16:30:09 +02:00 committed by GitHub
commit ad065e9281
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 212 additions and 130 deletions

View File

@ -49,12 +49,13 @@ ext {
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
}
dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:fef71aeccc37'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.8.9'

View File

@ -76,10 +76,6 @@
android:name=".about.AboutActivity"
android:label="@string/title_activity_about"/>
<activity
android:name=".history.HistoryActivity"
android:label="@string/title_activity_history"/>
<service android:name=".local.subscription.services.SubscriptionsImportService"/>
<service android:name=".local.subscription.services.SubscriptionsExportService"/>
@ -122,6 +118,7 @@
<activity
android:name=".ReCaptchaActivity"
android:label="@string/reCaptchaActivity"/>
<activity android:name=".download.ExtSDDownloadFailedActivity" />
<provider
android:name="android.support.v4.content.FileProvider"

View File

@ -0,0 +1,38 @@
package org.schabi.newpipe.download;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class ExtSDDownloadFailedActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
}
@Override
protected void onStart() {
super.onStart();
new AlertDialog.Builder(this)
.setTitle(R.string.download_to_sdcard_error_title)
.setMessage(R.string.download_to_sdcard_error_message)
.setPositiveButton(R.string.yes, (DialogInterface dialogInterface, int i) -> {
NewPipeSettings.resetDownloadFolders(this);
finish();
})
.setNegativeButton(R.string.cancel, (DialogInterface dialogInterface, int i) -> {
dialogInterface.dismiss();
finish();
})
.create()
.show();
}
}

View File

@ -87,12 +87,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
RxView.clicks(errorButtonRetry)
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
onRetryButtonClicked();
}
});
.subscribe(o -> onRetryButtonClicked());
}
protected void onRetryButtonClicked() {

View File

@ -1227,10 +1227,10 @@ public class VideoDetailFragment
spinnerToolbar.setVisibility(View.GONE);
break;
default:
if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
if (!info.getVideoStreams().isEmpty()
|| !info.getVideoOnlyStreams().isEmpty()) break;
detailControlsBackground.setVisibility(View.GONE);
detailControlsPopup.setVisibility(View.GONE);
spinnerToolbar.setVisibility(View.GONE);
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp);

View File

@ -169,38 +169,35 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
if (getFragmentManager() != null) {
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
if (getFragmentManager() != null) {
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
};
@ -258,12 +255,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
private void monitorSubscription(final ChannelInfo info) {
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
final Consumer<Throwable> onError = (Throwable throwable) -> {
animateView(headerSubscribeButton, false, 100);
showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0);
}
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(currentInfo.getServiceId()),
"Get subscription status",
0);
};
final Observable<List<SubscriptionEntity>> observable = subscriptionService.subscriptionTable()
@ -279,50 +276,38 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
// so only update the UI for the latest emission ("sync" the subscribe button's state)
.debounce(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<SubscriptionEntity>>() {
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
updateSubscribeButton(!subscriptionEntities.isEmpty());
}
}, onError));
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
updateSubscribeButton(!subscriptionEntities.isEmpty())
, onError));
}
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() {
@Override
public Object apply(@NonNull Object o) throws Exception {
subscriptionService.subscriptionTable().insert(subscription);
return o;
}
return (@NonNull Object o) -> {
subscriptionService.subscriptionTable().insert(subscription);
return o;
};
}
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() {
@Override
public Object apply(@NonNull Object o) throws Exception {
subscriptionService.subscriptionTable().delete(subscription);
return o;
}
return (@NonNull Object o) -> {
subscriptionService.subscriptionTable().delete(subscription);
return o;
};
}
private void updateSubscription(final ChannelInfo info) {
if (DEBUG) Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
final Action onComplete = new Action() {
@Override
public void run() throws Exception {
final Action onComplete = () -> {
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
}
};
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.getServiceId()), "Updating Subscription for " + info.getUrl(), R.string.subscription_update_failed);
}
};
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(info.getServiceId()),
"Updating Subscription for " + info.getUrl(),
R.string.subscription_update_failed);
disposables.add(subscriptionService.updateChannelInfo(info)
.subscribeOn(Schedulers.io())
@ -331,19 +316,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
}
private Disposable monitorSubscribeButton(final Button subscribeButton, final Function<Object, Object> action) {
final Consumer<Object> onNext = new Consumer<Object>() {
@Override
public void accept(@NonNull Object o) throws Exception {
final Consumer<Object> onNext = (@NonNull Object o) -> {
if (DEBUG) Log.d(TAG, "Changed subscription status to this channel!");
}
};
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Subscription Change", R.string.subscription_change_failed);
}
};
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(currentInfo.getServiceId()),
"Subscription Change",
R.string.subscription_change_failed);
/* Emit clicks from main thread unto io thread */
return RxView.clicks(subscribeButton)
@ -355,25 +337,25 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
}
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
return new Consumer<List<SubscriptionEntity>>() {
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
if (DEBUG)
Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
return (List<SubscriptionEntity> subscriptionEntities) -> {
if (DEBUG)
Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity();
channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl());
channel.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
} else {
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
}
if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity();
channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl());
channel.setData(info.getName(),
info.getAvatarUrl(),
info.getDescription(),
info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
} else {
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
}
};
}
@ -491,8 +473,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
super.handleNextItems(result);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
"Get next page of: " + url, R.string.general_error);
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
}

View File

@ -365,7 +365,7 @@ public class SearchFragment
int itemId = 0;
boolean isFirstItem = true;
final Context c = getContext();
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) {
for(String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1,
itemId++,
@ -575,8 +575,7 @@ public class SearchFragment
.onNext(searchEditText.getText().toString()),
throwable -> showSnackBarError(throwable,
UserAction.DELETE_FROM_HISTORY, "none",
"Deleting item failed", R.string.general_error)
);
"Deleting item failed", R.string.general_error));
disposables.add(onDelete);
})
.show();
@ -837,7 +836,10 @@ public class SearchFragment
@Override
public void handleResult(@NonNull SearchInfo result) {
if (!result.getErrors().isEmpty()) {
final List<Throwable> exceptions = result.getErrors();
if (!exceptions.isEmpty()
&& !(exceptions.size() == 1
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)){
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, 0);
}
@ -864,6 +866,7 @@ public class SearchFragment
showListFooter(false);
currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems());
nextPageUrl = result.getNextPageUrl();
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,

View File

@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -343,6 +344,7 @@ public final class BackgroundPlayer extends Service {
if (!shouldUpdateOnProgress) return;
resetNotification();
if(Build.VERSION.SDK_INT >= 26 /*Oreo*/) updateNotificationThumbnail();
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration));

View File

@ -51,6 +51,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
@ -69,6 +70,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.SerializedCache;
@ -86,6 +88,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
import static org.schabi.newpipe.report.UserAction.PLAY_STREAM;
/**
* Base for the players, joining the common properties
@ -96,7 +99,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
public abstract class BasePlayer implements
Player.EventListener, PlaybackListener, ImageLoadingListener {
public static final boolean DEBUG = true;
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@NonNull public static final String TAG = "BasePlayer";
@NonNull final protected Context context;
@ -363,7 +366,10 @@ public abstract class BasePlayer implements
try {
context.unregisterReceiver(broadcastReceiver);
} catch (final IllegalArgumentException unregisteredException) {
Log.e(TAG, "Broadcast receiver already unregistered.", unregisteredException);
ErrorActivity.reportError(context, unregisteredException, null, null,
ErrorActivity.ErrorInfo.make(PLAY_STREAM,
"none",
"play stream", R.string.general_error));
}
}
@ -1001,6 +1007,8 @@ public abstract class BasePlayer implements
try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) {
if(DEBUG) Log.d(TAG, "Could not update metadata: " + error.getMessage());
if(DEBUG) error.printStackTrace();
return;
}
@ -1087,6 +1095,9 @@ public abstract class BasePlayer implements
return simpleExoPlayer.isCurrentWindowDynamic();
} catch (@NonNull IndexOutOfBoundsException ignored) {
// Why would this even happen =(
// But lets log it anyway. Save is save
if(DEBUG) Log.d(TAG, "Could not update metadata: " + ignored.getMessage());
if(DEBUG) ignored.printStackTrace();
return false;
}
}

View File

@ -16,6 +16,7 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
@ -562,6 +563,12 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
if (player != null) {
progressLiveSync.setClickable(!player.isLiveEdge());
}
// this will make shure progressCurrentTime has the same width as progressEndTime
final ViewGroup.LayoutParams endTimeParams = progressEndTime.getLayoutParams();
final ViewGroup.LayoutParams currentTimeParams = progressCurrentTime.getLayoutParams();
currentTimeParams.width = progressEndTime.getWidth();
progressCurrentTime.setLayoutParams(currentTimeParams);
}
@Override

View File

@ -6,6 +6,7 @@ import android.util.Log;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.player.playqueue.events.AppendEvent;
import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
import org.schabi.newpipe.player.playqueue.events.InitEvent;
@ -41,7 +42,7 @@ import io.reactivex.subjects.BehaviorSubject;
public abstract class PlayQueue implements Serializable {
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
public static final boolean DEBUG = true;
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private ArrayList<PlayQueueItem> backup;
private ArrayList<PlayQueueItem> streams;

View File

@ -15,7 +15,8 @@ public enum UserAction {
REQUESTED_CHANNEL("requested channel"),
REQUESTED_PLAYLIST("requested playlist"),
REQUESTED_KIOSK("requested kiosk"),
DELETE_FROM_HISTORY("delete from history");
DELETE_FROM_HISTORY("delete from history"),
PLAY_STREAM("Play stream");
private final String message;

View File

@ -71,7 +71,7 @@ public class NewPipeSettings {
}
public static File getVideoDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
return getDir(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
}
public static String getVideoDownloadPath(Context context) {
@ -81,7 +81,7 @@ public class NewPipeSettings {
}
public static File getAudioDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
return getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
}
public static String getAudioDownloadPath(Context context) {
@ -90,21 +90,37 @@ public class NewPipeSettings {
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
}
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
private static File getDir(Context context, int keyID, String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
final File folder = getFolder(defaultDirectoryName);
final File dir = getDir(defaultDirectoryName);
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, new File(folder, "NewPipe").getAbsolutePath());
spEditor.putString(key, getNewPipeChildFolderPathForDir(dir));
spEditor.apply();
return folder;
return dir;
}
@NonNull
private static File getFolder(String defaultDirectoryName) {
private static File getDir(String defaultDirectoryName) {
return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName);
}
public static void resetDownloadFolders(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
resetDownloadFolder(prefs, context.getString(R.string.download_path_audio_key), Environment.DIRECTORY_MUSIC);
resetDownloadFolder(prefs, context.getString(R.string.download_path_key), Environment.DIRECTORY_MOVIES);
}
private static void resetDownloadFolder(SharedPreferences prefs, String key, String defaultDirectoryName) {
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
spEditor.apply();
}
private static String getNewPipeChildFolderPathForDir(File dir) {
return new File(dir, "NewPipe").getAbsolutePath();
}
}

View File

@ -73,7 +73,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry));
}
@ -88,7 +88,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl));

View File

@ -31,8 +31,6 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;

View File

@ -1,10 +1,17 @@
package us.shandian.giga.get;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.schabi.newpipe.download.ExtSDDownloadFailedActivity;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
@ -23,7 +30,9 @@ 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<>();
@NonNull
private final Context context;
/**
* Create a new instance
@ -33,6 +42,13 @@ public class DownloadManagerImpl implements DownloadManager {
*/
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource;
this.context = null;
loadMissions(searchLocations);
}
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource, Context context) {
mDownloadDataSource = downloadDataSource;
this.context = context;
loadMissions(searchLocations);
}
@ -277,10 +293,12 @@ public class DownloadManagerImpl implements DownloadManager {
}
private class Initializer extends Thread {
private DownloadMission mission;
private final DownloadMission mission;
private final Handler handler;
public Initializer(DownloadMission mission) {
this.mission = mission;
this.handler = new Handler();
}
@Override
@ -335,6 +353,13 @@ public class DownloadManagerImpl implements DownloadManager {
af.close();
mission.start();
} catch (IOException ie) {
if(context == null) throw new RuntimeException(ie);
if(ie.getMessage().contains("Permission denied")) {
handler.post(() ->
context.startActivity(new Intent(context, ExtSDDownloadFailedActivity.class)));
} else throw new RuntimeException(ie);
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);

View File

@ -81,7 +81,7 @@ public class DownloadManagerService extends Service {
ArrayList<String> paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this));
mManager = new DownloadManagerImpl(paths, mDataSource);
mManager = new DownloadManagerImpl(paths, mDataSource, this);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths);

View File

@ -170,6 +170,8 @@
<string name="search_history_deleted">Search history deleted.</string>
<!-- error strings -->
<string name="general_error">Error</string>
<string name="download_to_sdcard_error_title">External storage not available.</string>
<string name="download_to_sdcard_error_message">Download to external SD Card is not possible yet. Should the download place be reset?</string>
<string name="network_error">Network error</string>
<string name="could_not_load_thumbnails">Could not load all thumbnails</string>
<string name="youtube_signature_decryption_error">Could not decrypt video URL signature</string>

View File

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files