Merge branch 'dev' into trending

This commit is contained in:
Christian Schabesberger 2017-09-23 20:39:01 +02:00 committed by GitHub
commit 90b9223aae
50 changed files with 792 additions and 77 deletions

View File

@ -13,6 +13,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* Check whether your issue/feature is already fixed/implemented * Check whether your issue/feature is already fixed/implemented
* If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome! * If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome!
* We use English for development. Issues in other languages will be closed and ignored. * We use English for development. Issues in other languages will be closed and ignored.
* Please only add *one* issue at a time. Do not put multiple issues into one thread.
## Bug Fixing ## Bug Fixing
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information. * If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information.

View File

@ -10,9 +10,6 @@ android:
# The SDK version used to compile NewPipe # The SDK version used to compile NewPipe
- android-26 - android-26
# Additional components
- extra-android-m2repository
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses: licenses:

View File

@ -1,34 +1,32 @@
<p align="center"><a href="https://newpipe.schabi.org"><img src="assets/new_pipe_icon_5.png" width="150"/></a></p>
<h2 align="center"><b>NewPipe</b></h2>
<h4 align="center">A free lightweight YouTube frontend for Android.</h4>
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png"/></a></p>
<p align="center">
<a href="https://github.com/TeamNewPipe/NewPipe" alt="GitHub release"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" /></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPL v3"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg" /></a>
<a href="https://travis-ci.org/TeamNewPipe/NewPipe" alt="Build Status"><img src="https://travis-ci.org/TeamNewPipe/NewPipe.svg" /></a>
<a href="https://hosted.weblate.org/engage/NewPipe/" alt="Translation Status"><img src="https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg" /></a>
<a href="http://webchat.freenode.net/?channels=%23newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/IRC%20chat-%23newpipe-brightgreen.svg" /></a>
</p>
<hr />
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<hr />
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS. WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
# NewPipe
NewPipe: A free lightweight YouTube frontend for Android.
[![NewPipe](app/src/main/res/mipmap-xhdpi/ic_launcher.png)](https://newpipe.schabi.org)
[![F-Droid](https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png)](https://f-droid.org/packages/org.schabi.newpipe/)
Project status:
[![Translation Status](https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg)](https://hosted.weblate.org/engage/NewPipe/)
[![Build Status](https://travis-ci.org/TeamNewPipe/NewPipe.svg)](https://travis-ci.org/TeamNewPipe/NewPipe)
## Donate
![Bitcoin](https://bitcoin.org/img/icons/logotop.svg)
![BitcoinQR](assets/16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh.png)
`16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh`
## Screenshots ## Screenshots
[<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png) [<img src="screenshots/shot_1.png" width=160>](screenshots/shot_1.png)
[<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png) [<img src="screenshots/shot_2.png" width=160>](screenshots/shot_2.png)
[<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png) [<img src="screenshots/shot_3.png" width=160>](screenshots/shot_3.png)
[<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png) [<img src="screenshots/shot_4.png" width=160>](screenshots/shot_4.png)
[<img src="screenshots/screenshot_5.png" width=160>](screenshots/screenshot_5.png) [<img src="screenshots/shot_5.png" width=160>](screenshots/shot_5.png)
[<img src="screenshots/screenshot_6.png" width=160>](screenshots/screenshot_6.png) [<img src="screenshots/shot_6.png" width=160>](screenshots/shot_6.png)
[<img src="screenshots/screenshot_7.png" width=160>](screenshots/screenshot_7.png) [<img src="screenshots/shot_7.png" width=160>](screenshots/shot_7.png)
[<img src="screenshots/screenshot_8.png" width=160>](screenshots/screenshot_8.png) [<img src="screenshots/shot_8.png" width=160>](screenshots/shot_8.png)
[<img src="screenshots/screenshot_9.png" width=160>](screenshots/screenshot_9.png) [<img src="screenshots/shot_9.png" width=160>](screenshots/shot_9.png)
[<img src="screenshots/shot_10.png" width=160>](screenshots/shot_10.png)
## Description ## Description
@ -39,7 +37,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search videos * Search videos
* Display general information about a video * Display general information about a video
* Watch YouTube videos * Watch YouTube videos
* Listen to YouTube videos (experimental) * Listen to YouTube videos
* Popup mode (floating player) * Popup mode (floating player)
* Select the streaming player to watch the video with * Select the streaming player to watch the video with
* Download videos * Download videos
@ -47,21 +45,23 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Open a video in Kodi * Open a video in Kodi
* Show Next/Related videos * Show Next/Related videos
* Search YouTube in a specific language * Search YouTube in a specific language
* Watch age restricted material * Watch/Block age restricted material
* Display general information about channels * Display general information about channels
* Search channels * Search channels
* Watch videos from a channel * Watch videos from a channel
* Orbot/Tor support (not yet directly) * Orbot/Tor support (not yet directly)
* 1080p/2k/4k support * 1080p/2k/4k support
* View history
* Subscribe to channels
* Search history
* Search/Watch Playlists
### Coming Features ### Coming Features
* Multiservice support (eg. SoundCloud)
* Bookmarks * Bookmarks
* View history * Watch as queues Playlists
* Search history * Queuing videos
* Subscribe to channels
* Search/Watch Playlists
* Queeing videos
* Subtitles support * Subtitles support
* livestream support * livestream support
* ... and many more * ... and many more
@ -75,6 +75,22 @@ The more is done the better it gets!
If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md). If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md).
## Donate
If you like NewPipe we'd be happy about a donation. You can either donate via Bitcoin or BountySource. For further information about donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate/).
<table>
<tr>
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin" /></td>
<td><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR Code" width="100px"/></td>
<td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td>
</tr>
<tr>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alz="Bountysource" width="190px" /></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visit NewPipe at bountysource.com" width="100px"/></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Check out how many bounties you can earn." /></a></td>
</tr>
</table>
## License ## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)

View File

@ -26,6 +26,9 @@ android {
debuggable true debuggable true
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
} }
beta {
applicationIdSuffix ".beta"
}
} }
lintOptions { lintOptions {
@ -62,7 +65,7 @@ dependencies {
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'de.hdodenhof:circleimageview:2.1.0' compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.github.nirhart:parallaxscroll:1.0' compile 'com.github.nirhart:parallaxscroll:1.0'
compile 'com.nononsenseapps:filepicker:3.0.0' compile 'com.nononsenseapps:filepicker:3.0.1'
compile 'com.google.android.exoplayer:exoplayer:r2.5.1' compile 'com.google.android.exoplayer:exoplayer:r2.5.1'
debugCompile 'com.facebook.stetho:stetho:1.5.0' debugCompile 'com.facebook.stetho:stetho:1.5.0'

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="NewPipe Beta"
tools:replace="android:label">
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -88,10 +88,14 @@
<service android:name="us.shandian.giga.service.DownloadManagerService"/> <service android:name="us.shandian.giga.service.DownloadManagerService"/>
<activity <activity
android:name="com.nononsenseapps.filepicker.FilePickerActivity" android:name=".util.FilePickerActivityHelper"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:theme="@style/FilePickerThemeDark">
android:theme="@style/FilePickerTheme"/> <intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity <activity
android:name=".ReCaptchaActivity" android:name=".ReCaptchaActivity"

View File

@ -43,9 +43,8 @@ public class RouterActivity extends AppCompatActivity {
} }
protected void handleUrl(String url) { protected void handleUrl(String url) {
try { boolean success = NavigationHelper.openByLink(this, url);
NavigationHelper.openByLink(this, url); if (!success) {
} catch (Exception e) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
} }

View File

@ -34,6 +34,7 @@ import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchEngine; import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult; import org.schabi.newpipe.extractor.search.SearchResult;
@ -47,12 +48,14 @@ import org.schabi.newpipe.util.StateSaver;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import icepick.State; import icepick.State;
import io.reactivex.Notification; import io.reactivex.Notification;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer; import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function; import io.reactivex.functions.Function;
@ -97,6 +100,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
private PublishSubject<String> suggestionPublisher = PublishSubject.create(); private PublishSubject<String> suggestionPublisher = PublishSubject.create();
private Disposable searchDisposable; private Disposable searchDisposable;
private Disposable suggestionWorkerDisposable; private Disposable suggestionWorkerDisposable;
private CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter; private SuggestionListAdapter suggestionListAdapter;
@ -149,6 +153,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
if (searchDisposable != null) searchDisposable.dispose(); if (searchDisposable != null) searchDisposable.dispose();
if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose(); if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose();
if (disposables != null) disposables.clear();
hideSoftKeyboard(searchEditText); hideSoftKeyboard(searchEditText);
} }
@ -192,6 +197,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
if (searchDisposable != null) searchDisposable.dispose(); if (searchDisposable != null) searchDisposable.dispose();
if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose(); if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose();
if (disposables != null) disposables.clear();
} }
@Override @Override
@ -514,6 +520,38 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
private void search(final String query) { private void search(final String query) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]"); if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]");
try {
final StreamingService service = NewPipe.getServiceByUrl(query);
if (service != null) {
showLoading();
disposables.add(Observable
.fromCallable(new Callable<Intent>() {
@Override
public Intent call() throws Exception {
return NavigationHelper.getIntentByLink(activity, service, query);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
showError(getString(R.string.url_not_supported_toast), false);
hideLoading();
}
}));
return;
}
} catch (Exception e) {
// Exception occurred, it's not a url
}
hideSoftKeyboard(searchEditText); hideSoftKeyboard(searchEditText);
this.searchQuery = query; this.searchQuery = query;
this.currentPage = 0; this.currentPage = 0;
@ -532,6 +570,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
@Override @Override
public void startLoading(boolean forceLoad) { public void startLoading(boolean forceLoad) {
super.startLoading(forceLoad); super.startLoading(forceLoad);
if (disposables != null) disposables.clear();
if (searchDisposable != null) searchDisposable.dispose(); if (searchDisposable != null) searchDisposable.dispose();
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter) searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -7,9 +7,8 @@ import android.support.annotation.Nullable;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.Log; import android.util.Log;
import com.nononsenseapps.filepicker.FilePickerActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.util.FilePickerActivityHelper;
public class DownloadSettingsFragment extends BasePreferenceFragment { public class DownloadSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_DOWNLOAD_PATH = 0x1235; private static final int REQUEST_DOWNLOAD_PATH = 0x1235;
@ -48,10 +47,10 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
} }
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
Intent i = new Intent(getActivity(), FilePickerActivity.class) Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false) .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true) .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR); .putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) { if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) {
startActivityForResult(i, REQUEST_DOWNLOAD_PATH); startActivityForResult(i, REQUEST_DOWNLOAD_PATH);
} else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { } else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {

View File

@ -0,0 +1,17 @@
package org.schabi.newpipe.util;
import android.os.Bundle;
import org.schabi.newpipe.R;
public class FilePickerActivityHelper extends com.nononsenseapps.filepicker.FilePickerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
if(ThemeHelper.isLightThemeSelected(this)) {
this.setTheme(R.style.FilePickerThemeLight);
} else {
this.setTheme(R.style.FilePickerThemeDark);
}
super.onCreate(savedInstanceState);
}
}

View File

@ -14,7 +14,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.about.AboutActivity; import org.schabi.newpipe.about.AboutActivity;
import org.schabi.newpipe.download.DownloadActivity; import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.MainFragment;
@ -228,13 +230,17 @@ public class NavigationHelper {
// Link handling // Link handling
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public static void openByLink(Context context, String url) throws Exception { public static boolean openByLink(Context context, String url) {
Intent intentByLink = getIntentByLink(context, url); Intent intentByLink;
if (intentByLink == null) try {
throw new NullPointerException("getIntentByLink(context = [" + context + "], url = [" + url + "]) returned null"); intentByLink = getIntentByLink(context, url);
} catch (ExtractionException e) {
return false;
}
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentByLink); context.startActivity(intentByLink);
return true;
} }
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) { private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
@ -245,14 +251,20 @@ public class NavigationHelper {
return mIntent; return mIntent;
} }
private static Intent getIntentByLink(Context context, String url) throws Exception { public static Intent getIntentByLink(Context context, String url) throws ExtractionException {
StreamingService service = NewPipe.getServiceByUrl(url); return getIntentByLink(context, NewPipe.getServiceByUrl(url), url);
}
public static Intent getIntentByLink(Context context, StreamingService service, String url) throws ExtractionException {
if (service != ServiceList.YouTube.getService()) {
throw new ExtractionException("Service not supported at the moment");
}
int serviceId = service.getServiceId(); int serviceId = service.getServiceId();
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
if (linkType == StreamingService.LinkType.NONE) { if (linkType == StreamingService.LinkType.NONE) {
throw new Exception("Url not known to service. service=" + serviceId + " url=" + url); throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url);
} }
url = getCleanUrl(service, url, linkType); url = getCleanUrl(service, url, linkType);
@ -268,7 +280,7 @@ public class NavigationHelper {
return rIntent; return rIntent;
} }
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws Exception { private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
switch (linkType) { switch (linkType) {
case STREAM: case STREAM:
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl); return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);

View File

@ -101,6 +101,18 @@ public class DownloadManagerImpl implements DownloadManager {
} }
/**
* Sort a list of mission by its timestamp. Oldest first
* @param missions the missions to sort
*/
static void sortByTimestamp(List<DownloadMission> missions) {
Collections.sort(missions, new Comparator<DownloadMission>() {
@Override
public int compare(DownloadMission o1, DownloadMission o2) {
return Long.valueOf(o1.timestamp).compareTo(o2.timestamp);
}
});
}
/** /**
* Loads finished missions from the data source * Loads finished missions from the data source
@ -111,12 +123,8 @@ public class DownloadManagerImpl implements DownloadManager {
finishedMissions = new ArrayList<>(); finishedMissions = new ArrayList<>();
} }
// Ensure its sorted // Ensure its sorted
Collections.sort(finishedMissions, new Comparator<DownloadMission>() { sortByTimestamp(finishedMissions);
@Override
public int compare(DownloadMission o1, DownloadMission o2) {
return (int) (o1.timestamp - o2.timestamp);
}
});
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size()); mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
for (DownloadMission mission : finishedMissions) { for (DownloadMission mission : finishedMissions) {
File downloadedFile = mission.getDownloadedFile(); File downloadedFile = mission.getDownloadedFile();

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#CD201F</color>
</resources>

View File

@ -51,7 +51,7 @@
<string-array name="video_format_description_list" translatable="false"> <string-array name="video_format_description_list" translatable="false">
<item>MPEG-4</item> <item>MPEG-4</item>
<item>WebM</item> <item>WebM</item>
<item>3GPP</item> <item>3GP</item>
</string-array> </string-array>
<string-array name="video_format_values_list" translatable="false"> <string-array name="video_format_values_list" translatable="false">
<item>@string/video_mp4_key</item> <item>@string/video_mp4_key</item>

View File

@ -109,23 +109,47 @@
<item name="background">@color/video_overlay_color</item> <item name="background">@color/video_overlay_color</item>
</style> </style>
<!-- You can also inherit from NNF_BaseTheme.Light --> <style name="FilePickerThemeLight" parent="NNF_BaseTheme.Light">
<style name="FilePickerTheme" parent="NNF_BaseTheme.Light">
<!-- Set these to match your theme -->
<item name="colorPrimary">@color/light_youtube_primary_color</item> <item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item> <item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item> <item name="colorAccent">@color/light_youtube_accent_color</item>
<item name="android:background">@color/light_background_color</item>
<item name="nnf_separator_color">@color/light_separator_color</item>
<!-- Need to set this also to style create folder dialog --> <item name="alertDialogTheme">@style/FilePickerAlertDialogThemeLight</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogTheme</item> <item name="nnf_toolbarTheme">@style/FilePickerToolbarLight</item>
<!-- If you want to set a specific toolbar theme, do it here -->
<!-- <item name="nnf_toolbarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> -->
</style> </style>
<style name="FilePickerAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert"> <style name="FilePickerAlertDialogThemeLight" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/light_youtube_primary_color</item> <item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item> <item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item> <item name="colorAccent">@color/light_youtube_accent_color</item>
</style> </style>
<style name="FilePickerToolbarLight" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/light_youtube_primary_color</item>
</style>
<style name="FilePickerThemeDark" parent="FilePickerThemeLight">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
<item name="android:background">@color/dark_background_color</item>
<item name="android:textColorPrimary">@color/dark_youtube_accent_color</item>
<item name="nnf_separator_color">@color/black_separator_color</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogThemeDark</item>
<item name="nnf_toolbarTheme">@style/FilePickerToolbarDark</item>
</style>
<style name="FilePickerAlertDialogThemeDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
</style>
<style name="FilePickerToolbarDark" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/dark_youtube_primary_color</item>
</style>
</resources> </resources>

View File

@ -1,4 +1,4 @@
package us.shandian.giga.get.get; package us.shandian.giga.get;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -153,4 +153,34 @@ public class DownloadManagerImplTest {
assertSame(missions.get(1), downloadManager.getMission(1)); assertSame(missions.get(1), downloadManager.getMission(1));
} }
@Test
public void sortByTimestamp() throws Exception {
ArrayList<DownloadMission> downloadMissions = new ArrayList<>();
DownloadMission mission = new DownloadMission();
mission.timestamp = 0;
DownloadMission mission1 = new DownloadMission();
mission1.timestamp = Integer.MAX_VALUE + 1L;
DownloadMission mission2 = new DownloadMission();
mission2.timestamp = 2L * Integer.MAX_VALUE ;
DownloadMission mission3 = new DownloadMission();
mission3.timestamp = 2L * Integer.MAX_VALUE + 5L;
downloadMissions.add(mission3);
downloadMissions.add(mission1);
downloadMissions.add(mission2);
downloadMissions.add(mission);
DownloadManagerImpl.sortByTimestamp(downloadMissions);
assertEquals(mission, downloadMissions.get(0));
assertEquals(mission1, downloadMissions.get(1));
assertEquals(mission2, downloadMissions.get(2));
assertEquals(mission3, downloadMissions.get(3));
}
} }

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -3,6 +3,7 @@
buildscript { buildscript {
repositories { repositories {
jcenter() jcenter()
google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.android.tools.build:gradle:2.3.3'
@ -16,7 +17,7 @@ allprojects {
repositories { repositories {
jcenter() jcenter()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://maven.google.com' } google()
maven { url 'https://clojars.org/repo' } maven { url 'https://clojars.org/repo' }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

BIN
screenshots/shot_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
screenshots/shot_10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
screenshots/shot_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
screenshots/shot_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

BIN
screenshots/shot_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
screenshots/shot_5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
screenshots/shot_6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
screenshots/shot_7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
screenshots/shot_8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
screenshots/shot_9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB