diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index d983a6f71..a003040c8 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -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
* 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.
+* Please only add *one* issue at a time. Do not put multiple issues into one thread.
## 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.
diff --git a/.github/PULL_REQUEST_TEAMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
similarity index 100%
rename from .github/PULL_REQUEST_TEAMPLATE.md
rename to .github/PULL_REQUEST_TEMPLATE.md
diff --git a/.travis.yml b/.travis.yml
index fad605996..e0fcfb82b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,9 +10,6 @@ android:
# The SDK version used to compile NewPipe
- android-26
- # Additional components
- - extra-android-m2repository
-
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses:
diff --git a/README.md b/README.md
index ff53078c7..a127b10a5 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,32 @@
+
+NewPipe
+A free lightweight YouTube frontend for Android.
+
+
+
+
+
+
+
+
+
+
+Screenshots • Description • Features • Contribution • Donate • License
+
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/screenshot_1.png)
-[ ](screenshots/screenshot_2.png)
-[ ](screenshots/screenshot_3.png)
-[ ](screenshots/screenshot_4.png)
-[ ](screenshots/screenshot_5.png)
-[ ](screenshots/screenshot_6.png)
-[ ](screenshots/screenshot_7.png)
-[ ](screenshots/screenshot_8.png)
-[ ](screenshots/screenshot_9.png)
-
+[ ](screenshots/shot_1.png)
+[ ](screenshots/shot_2.png)
+[ ](screenshots/shot_3.png)
+[ ](screenshots/shot_4.png)
+[ ](screenshots/shot_5.png)
+[ ](screenshots/shot_6.png)
+[ ](screenshots/shot_7.png)
+[ ](screenshots/shot_8.png)
+[ ](screenshots/shot_9.png)
+[ ](screenshots/shot_10.png)
## Description
@@ -39,7 +37,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search videos
* Display general information about a video
* Watch YouTube videos
-* Listen to YouTube videos (experimental)
+* Listen to YouTube videos
* Popup mode (floating player)
* Select the streaming player to watch the video with
* 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
* Show Next/Related videos
* Search YouTube in a specific language
-* Watch age restricted material
+* Watch/Block age restricted material
* Display general information about channels
* Search channels
* Watch videos from a channel
* Orbot/Tor support (not yet directly)
* 1080p/2k/4k support
+* View history
+* Subscribe to channels
+* Search history
+* Search/Watch Playlists
### Coming Features
+* Multiservice support (eg. SoundCloud)
* Bookmarks
-* View history
-* Search history
-* Subscribe to channels
-* Search/Watch Playlists
-* Queeing videos
+* Watch as queues Playlists
+* Queuing videos
* Subtitles support
* livestream support
* ... 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).
+## 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/).
+
+
+
+
+
+16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh
+
+
+
+
+
+
+
+
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
diff --git a/app/build.gradle b/app/build.gradle
index 941dc72d7..5d718ea0e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 26
- versionCode 38
- versionName "0.10.0"
+ versionCode 39
+ versionName "0.10.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -26,6 +26,9 @@ android {
debuggable true
applicationIdSuffix ".debug"
}
+ beta {
+ applicationIdSuffix ".beta"
+ }
}
lintOptions {
@@ -45,7 +48,7 @@ dependencies {
exclude module: 'support-annotations'
}
- compile 'com.github.TeamNewPipe:NewPipeExtractor:7ae274b'
+ compile 'com.github.TeamNewPipe:NewPipeExtractor:1df3f67'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
@@ -62,7 +65,7 @@ dependencies {
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'de.hdodenhof:circleimageview:2.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'
debugCompile 'com.facebook.stetho:stetho:1.5.0'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index d38a631a2..1b2ac6835 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -24,4 +24,14 @@
-dontwarn org.mozilla.javascript.tools.**
-dontwarn android.arch.util.paging.CountedDataSource
--dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
\ No newline at end of file
+-dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
+
+
+# Rules for icepick. Copy paste from https://github.com/frankiesardo/icepick
+-dontwarn icepick.**
+-keep class icepick.** { *; }
+-keep class **$$Icepick { *; }
+-keepclasseswithmembernames class * {
+ @icepick.* ;
+}
+-keepnames class * { @icepick.State *;}
diff --git a/app/src/beta/AndroidManifest.xml b/app/src/beta/AndroidManifest.xml
new file mode 100644
index 000000000..dd390a318
--- /dev/null
+++ b/app/src/beta/AndroidManifest.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/beta/res/mipmap-hdpi/ic_launcher.png b/app/src/beta/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..73c65771d
Binary files /dev/null and b/app/src/beta/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/beta/res/mipmap-mdpi/ic_launcher.png b/app/src/beta/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..6f5eeeee1
Binary files /dev/null and b/app/src/beta/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/beta/res/mipmap-xhdpi/ic_launcher.png b/app/src/beta/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..e871a3ea9
Binary files /dev/null and b/app/src/beta/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/beta/res/mipmap-xxhdpi/ic_launcher.png b/app/src/beta/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..e38901a86
Binary files /dev/null and b/app/src/beta/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/beta/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/beta/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..1cf7e64be
Binary files /dev/null and b/app/src/beta/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 826ae4f44..8ae994de7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -88,10 +88,14 @@
+ android:theme="@style/FilePickerThemeDark">
+
+
+
+
+
> entry : con.getHeaderFields().entrySet()) {
- System.err.println(entry.getKey() + ": " + entry.getValue());
- }
- String inputLine;
+ String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index 434a34c7e..ce9c3802f 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -43,9 +43,8 @@ public class RouterActivity extends AppCompatActivity {
}
protected void handleUrl(String url) {
- try {
- NavigationHelper.openByLink(this, url);
- } catch (Exception e) {
+ boolean success = NavigationHelper.openByLink(this, url);
+ if (!success) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
}
diff --git a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java
index 921ce63a1..70799d971 100644
--- a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java
+++ b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java
@@ -11,6 +11,7 @@ import io.reactivex.Flowable;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID;
+import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME;
@@ -27,11 +28,20 @@ public interface SearchHistoryDAO extends HistoryDAO {
@Override
int deleteAll();
+ @Query("DELETE FROM " + TABLE_NAME + " WHERE " + SEARCH + " = :query")
+ int deleteAllWhereQuery(String query);
+
@Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE)
@Override
Flowable> getAll();
+ @Query("SELECT * FROM " + TABLE_NAME + " GROUP BY " + SEARCH + ORDER_BY_CREATION_DATE + " LIMIT :limit")
+ Flowable> getUniqueEntries(int limit);
+
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE)
@Override
Flowable> listByService(int serviceId);
+
+ @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SEARCH + " LIKE :query || '%' GROUP BY " + SEARCH + " LIMIT :limit")
+ Flowable> getSimilarEntries(String query, int limit);
}
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
index 567bec309..12d1764cc 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java
@@ -7,6 +7,7 @@ import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
+import org.schabi.newpipe.util.Constants;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE;
@@ -28,7 +29,7 @@ public class SubscriptionEntity {
private long uid = 0;
@ColumnInfo(name = SUBSCRIPTION_SERVICE_ID)
- private int serviceId = -1;
+ private int serviceId = Constants.NO_SERVICE_ID;
@ColumnInfo(name = SUBSCRIPTION_URL)
private String url;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 5f954cad2..4bb0c2cca 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -65,6 +65,7 @@ import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
+import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.InfoCache;
import org.schabi.newpipe.util.ListHelper;
@@ -110,7 +111,7 @@ public class VideoDetailFragment extends BaseStateFragment implement
private boolean wasRelatedStreamsExpanded = false;
@State
- protected int serviceId = -1;
+ protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
index 34fcaf873..4baf323ff 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
@@ -8,6 +8,7 @@ import android.view.View;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
+import org.schabi.newpipe.util.Constants;
import java.util.Queue;
@@ -21,7 +22,7 @@ import io.reactivex.schedulers.Schedulers;
public abstract class BaseListInfoFragment extends BaseListFragment {
@State
- protected int serviceId = -1;
+ protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index 653c50109..90d4d9741 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -2,14 +2,16 @@ package org.schabi.newpipe.fragments.list.search;
import android.app.Activity;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.TooltipCompat;
import android.text.Editable;
import android.text.TextUtils;
@@ -25,35 +27,50 @@ import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
import android.widget.TextView;
+import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
+import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
+import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
+import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
+import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.history.HistoryListener;
import org.schabi.newpipe.report.UserAction;
+import org.schabi.newpipe.util.Constants;
+import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
+import org.schabi.newpipe.util.LayoutManagerSmoothScroller;
import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.StateSaver;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Queue;
+import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import icepick.State;
+import io.reactivex.Flowable;
import io.reactivex.Notification;
import io.reactivex.Observable;
+import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
@@ -62,69 +79,78 @@ import io.reactivex.subjects.PublishSubject;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
-public class SearchFragment extends BaseListFragment {
+public class SearchFragment extends BaseListFragment implements BackPressable {
/*//////////////////////////////////////////////////////////////////////////
// Search
//////////////////////////////////////////////////////////////////////////*/
/**
- * The suggestions will appear only if the query meet this threshold (>=).
+ * The suggestions will only be fetched from network if the query meet this threshold (>=).
+ * (local ones will be fetched regardless of the length)
*/
- private static final int THRESHOLD_SUGGESTION = 3;
+ private static final int THRESHOLD_NETWORK_SUGGESTION = 1;
/**
* How much time have to pass without emitting a item (i.e. the user stop typing) to fetch/show the suggestions, in milliseconds.
*/
- private static final int SUGGESTIONS_DEBOUNCE = 150; //ms
+ private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
@State
protected int filterItemCheckedId = -1;
private SearchEngine.Filter filter = SearchEngine.Filter.ANY;
@State
- protected int serviceId = -1;
+ protected int serviceId = Constants.NO_SERVICE_ID;
@State
- protected String searchQuery = "";
+ protected String searchQuery;
+ @State
+ protected String lastSearchedQuery;
@State
protected boolean wasSearchFocused = false;
private int currentPage = 0;
private int currentNextPage = 0;
private String searchLanguage;
- private boolean showSuggestions = true;
+ private boolean isSuggestionsEnabled = true;
private PublishSubject suggestionPublisher = PublishSubject.create();
private Disposable searchDisposable;
- private Disposable suggestionWorkerDisposable;
+ private Disposable suggestionDisposable;
+ private CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter;
+ private SearchHistoryDAO searchHistoryDAO;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private View searchToolbarContainer;
- private AutoCompleteTextView searchEditText;
+ private EditText searchEditText;
private View searchClear;
+ private View suggestionsPanel;
+ private RecyclerView suggestionsRecyclerView;
+
/*////////////////////////////////////////////////////////////////////////*/
public static SearchFragment getInstance(int serviceId, String query) {
SearchFragment searchFragment = new SearchFragment();
searchFragment.setQuery(serviceId, query);
- searchFragment.searchOnResume();
+
+ if (!TextUtils.isEmpty(query)) {
+ searchFragment.setSearchOnResume();
+ }
+
return searchFragment;
}
/**
* Set wasLoading to true so when the fragment onResume is called, the initial search is done.
- * (it will only start searching if the query is not null or empty)
*/
- private void searchOnResume() {
- if (!TextUtils.isEmpty(searchQuery)) {
- wasLoading.set(true);
- }
+ private void setSearchOnResume() {
+ wasLoading.set(true);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -135,6 +161,16 @@ public class SearchFragment extends BaseListFragment currentPage) loadMoreItems();
@@ -175,7 +216,16 @@ public class SearchFragment extends BaseListFragment= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- searchEditText.setText("", false);
- } else searchEditText.setText("");
- suggestionListAdapter.updateAdapter(new ArrayList());
- showSoftKeyboard(searchEditText);
+ searchEditText.setText("");
+ suggestionListAdapter.setItems(new ArrayList());
+ showKeyboardSearch();
}
});
@@ -366,7 +417,9 @@ public class SearchFragment extends BaseListFragment parent, View view, int position, long id) {
- if (DEBUG) {
- Log.d(TAG, "onItemClick() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
- }
- String s = suggestionListAdapter.getSuggestion(position);
- if (DEBUG) Log.d(TAG, "onItemClick text = " + s);
- submitQuery(s);
+ public void onSuggestionItemSelected(SuggestionItem item) {
+ search(item.query);
+ searchEditText.setText(item.query);
+ }
+
+ @Override
+ public void onSuggestionItemLongClick(SuggestionItem item) {
+ if (item.fromHistory) showDeleteSuggestionDialog(item);
}
});
- searchEditText.setThreshold(THRESHOLD_SUGGESTION);
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
textWatcher = new TextWatcher() {
@@ -404,32 +459,32 @@ public class SearchFragment extends BaseListFragment() {
+ @Override
+ public Integer call() throws Exception {
+ return searchHistoryDAO.deleteAllWhereQuery(item.query);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Consumer() {
+ @Override
+ public void accept(Integer howManyDeleted) throws Exception {
+ suggestionPublisher.onNext(searchEditText.getText().toString());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ showSnackBarError(throwable, UserAction.SOMETHING_ELSE, "none", "Deleting item failed", R.string.general_error);
+ }
+ }));
+ }
+ }).show();
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) {
+ hideSuggestionsPanel();
+ hideKeyboardSearch();
+ searchEditText.setText(lastSearchedQuery);
+ return true;
+ }
+ return false;
}
public void giveSearchEditTextFocus() {
- showSoftKeyboard(searchEditText);
+ showKeyboardSearch();
}
private void initSuggestionObserver() {
- if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose();
- final Predicate checkEnabledAndLength = new Predicate() {
- @Override
- public boolean test(@io.reactivex.annotations.NonNull String s) throws Exception {
- boolean lengthCheck = s.length() >= THRESHOLD_SUGGESTION;
- // Clear the suggestions adapter if the length check fails
- if (!lengthCheck && !suggestionListAdapter.isEmpty()) {
- suggestionListAdapter.updateAdapter(new ArrayList());
- }
- // Only pass through if suggestions is enabled and the query length is equal or greater than THRESHOLD_SUGGESTION
- return showSuggestions && lengthCheck;
- }
- };
+ if (DEBUG) Log.d(TAG, "initSuggestionObserver() called");
+ if (suggestionDisposable != null) suggestionDisposable.dispose();
- suggestionWorkerDisposable = suggestionPublisher
+ final Observable observable = suggestionPublisher
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
- .startWith(!TextUtils.isEmpty(searchQuery) ? searchQuery : "")
- .filter(checkEnabledAndLength)
- .switchMap(new Function>>>() {
+ .startWith(searchQuery != null ? searchQuery : "")
+ .filter(new Predicate() {
@Override
- public Observable>> apply(@io.reactivex.annotations.NonNull String query) throws Exception {
- return ExtractorHelper.suggestionsFor(serviceId, query, searchLanguage).toObservable().materialize();
+ public boolean test(@io.reactivex.annotations.NonNull String query) throws Exception {
+ return isSuggestionsEnabled;
+ }
+ });
+
+ suggestionDisposable = observable
+ .switchMap(new Function>>>() {
+ @Override
+ public ObservableSource>> apply(@io.reactivex.annotations.NonNull final String query) throws Exception {
+ final Flowable> flowable = query.length() > 0
+ ? searchHistoryDAO.getSimilarEntries(query, 3)
+ : searchHistoryDAO.getUniqueEntries(25);
+ final Observable> local = flowable.toObservable()
+ .map(new Function, List>() {
+ @Override
+ public List apply(@io.reactivex.annotations.NonNull List searchHistoryEntries) throws Exception {
+ List result = new ArrayList<>();
+ for (SearchHistoryEntry entry : searchHistoryEntries)
+ result.add(new SuggestionItem(true, entry.getSearch()));
+ return result;
+ }
+ });
+
+ if (query.length() < THRESHOLD_NETWORK_SUGGESTION) {
+ // Only pass through if the query length is equal or greater than THRESHOLD_NETWORK_SUGGESTION
+ return local.materialize();
+ }
+
+ final Observable> network = ExtractorHelper.suggestionsFor(serviceId, query, searchLanguage).toObservable()
+ .map(new Function, List>() {
+ @Override
+ public List apply(@io.reactivex.annotations.NonNull List strings) throws Exception {
+ List result = new ArrayList<>();
+ for (String entry : strings) result.add(new SuggestionItem(false, entry));
+ return result;
+ }
+ });
+
+ return Observable.zip(local, network, new BiFunction, List, List>() {
+ @Override
+ public List apply(@io.reactivex.annotations.NonNull List localResult, @io.reactivex.annotations.NonNull List networkResult) throws Exception {
+ List result = new ArrayList<>();
+ if (localResult.size() > 0) result.addAll(localResult);
+
+ // Remove duplicates
+ final Iterator iterator = networkResult.iterator();
+ while (iterator.hasNext() && localResult.size() > 0) {
+ final SuggestionItem next = iterator.next();
+ for (SuggestionItem item : localResult) {
+ if (item.query.equals(next.query)) {
+ iterator.remove();
+ break;
+ }
+ }
+ }
+
+ if (networkResult.size() > 0) result.addAll(networkResult);
+ return result;
+ }
+ }).materialize();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Consumer>>() {
+ .subscribe(new Consumer>>() {
@Override
- public void accept(@io.reactivex.annotations.NonNull Notification> listNotification) throws Exception {
+ public void accept(@io.reactivex.annotations.NonNull Notification> listNotification) throws Exception {
if (listNotification.isOnNext()) {
handleSuggestions(listNotification.getValue());
- if (errorPanelRoot.getVisibility() == View.VISIBLE) {
- hideLoading();
- }
} else if (listNotification.isOnError()) {
Throwable error = listNotification.getError();
- if (!ExtractorHelper.isInterruptedCaused(error)) {
+ if (!ExtractorHelper.hasAssignableCauseThrowable(error,
+ IOException.class, SocketException.class, InterruptedException.class, InterruptedIOException.class)) {
onSuggestionError(error);
}
}
@@ -513,25 +666,58 @@ public class SearchFragment extends BaseListFragment() {
+ @Override
+ public Intent call() throws Exception {
+ return NavigationHelper.getIntentByLink(activity, service, query);
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Consumer() {
+ @Override
+ public void accept(Intent intent) throws Exception {
+ getFragmentManager().popBackStackImmediate();
+ activity.startActivity(intent);
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ showError(getString(R.string.url_not_supported_toast), false);
+ }
+ }));
+ return;
+ }
+ } catch (Exception e) {
+ // Exception occurred, it's not a url
+ }
+
+ lastSearchedQuery = query;
+ searchQuery = query;
+ currentPage = 0;
infoListAdapter.clearStreamItemList();
+ hideSuggestionsPanel();
+ hideKeyboardSearch();
if (activity instanceof HistoryListener) {
((HistoryListener) activity).onSearch(serviceId, query);
+ suggestionPublisher.onNext(query);
}
- final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
- final String searchLanguageKey = getContext().getString(R.string.search_language_key);
- searchLanguage = sharedPreferences.getString(searchLanguageKey, getContext().getString(R.string.default_language_value));
startLoading(false);
}
@Override
public void startLoading(boolean forceLoad) {
super.startLoading(forceLoad);
+ if (disposables != null) disposables.clear();
if (searchDisposable != null) searchDisposable.dispose();
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter)
.subscribeOn(Schedulers.io())
@@ -584,7 +770,7 @@ public class SearchFragment extends BaseListFragment suggestions) {
+ public void handleSuggestions(@NonNull final List suggestions) {
if (DEBUG) Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
- suggestionListAdapter.updateAdapter(suggestions);
+ suggestionsRecyclerView.smoothScrollToPosition(0);
+ suggestionsRecyclerView.post(new Runnable() {
+ @Override
+ public void run() {
+ suggestionListAdapter.setItems(suggestions);
+ }
+ });
+
+ if (errorPanelRoot.getVisibility() == View.VISIBLE) {
+ hideLoading();
+ }
}
public void onSuggestionError(Throwable exception) {
@@ -642,6 +829,13 @@ public class SearchFragment extends BaseListFragment 0) {
infoListAdapter.addInfoItemList(result.resultList);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java
new file mode 100644
index 000000000..722638926
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java
@@ -0,0 +1,16 @@
+package org.schabi.newpipe.fragments.list.search;
+
+public class SuggestionItem {
+ public final boolean fromHistory;
+ public final String query;
+
+ public SuggestionItem(boolean fromHistory, String query) {
+ this.fromHistory = fromHistory;
+ this.query = query;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + fromHistory + "→" + query + "]";
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java
index 0a7e3d613..71d9bf780 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java
@@ -1,89 +1,108 @@
package org.schabi.newpipe.fragments.list.search;
import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.support.v4.widget.ResourceCursorAdapter;
+import android.content.res.TypedArray;
+import android.support.annotation.AttrRes;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.TextView;
+import org.schabi.newpipe.R;
+
+import java.util.ArrayList;
import java.util.List;
-/*
- * Created by Christian Schabesberger on 02.08.16.
- *
- * Copyright (C) Christian Schabesberger 2016
- * SuggestionListAdapter.java is part of NewPipe.
- *
- * NewPipe is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * NewPipe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with NewPipe. If not, see .
- */
-
-/**
- * {@link ResourceCursorAdapter} to display suggestions.
- */
-public class SuggestionListAdapter extends ResourceCursorAdapter {
-
- private static final String[] columns = new String[]{"_id", "title"};
- private static final int INDEX_ID = 0;
- private static final int INDEX_TITLE = 1;
+public class SuggestionListAdapter extends RecyclerView.Adapter {
+ private final ArrayList items = new ArrayList<>();
+ private final Context context;
+ private OnSuggestionItemSelected listener;
+ public interface OnSuggestionItemSelected {
+ void onSuggestionItemSelected(SuggestionItem item);
+ void onSuggestionItemLongClick(SuggestionItem item);
+ }
public SuggestionListAdapter(Context context) {
- super(context, android.R.layout.simple_list_item_1, null, 0);
+ this.context = context;
+ }
+
+ public void setItems(List items) {
+ this.items.clear();
+ this.items.addAll(items);
+ notifyDataSetChanged();
+ }
+
+ public void setListener(OnSuggestionItemSelected listener) {
+ this.listener = listener;
}
@Override
- public void bindView(View view, Context context, Cursor cursor) {
- ViewHolder viewHolder = new ViewHolder(view);
- viewHolder.suggestionTitle.setText(cursor.getString(INDEX_TITLE));
- }
-
- /**
- * Update the suggestion list
- * @param suggestions the list of suggestions
- */
- public void updateAdapter(List suggestions) {
- MatrixCursor cursor = new MatrixCursor(columns, suggestions.size());
- int i = 0;
- for (String suggestion : suggestions) {
- String[] columnValues = new String[columns.length];
- columnValues[INDEX_TITLE] = suggestion;
- columnValues[INDEX_ID] = Integer.toString(i);
- cursor.addRow(columnValues);
- i++;
- }
- changeCursor(cursor);
- }
-
- /**
- * Get the suggestion for a position
- * @param position the position of the suggestion
- * @return the suggestion
- */
- public String getSuggestion(int position) {
- return ((Cursor) getItem(position)).getString(INDEX_TITLE);
+ public SuggestionItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new SuggestionItemHolder(LayoutInflater.from(context).inflate(R.layout.item_search_suggestion, parent, false));
}
@Override
- public CharSequence convertToString(Cursor cursor) {
- return cursor.getString(INDEX_TITLE);
+ public void onBindViewHolder(SuggestionItemHolder holder, int position) {
+ final SuggestionItem currentItem = getItem(position);
+ holder.updateFrom(currentItem);
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) listener.onSuggestionItemSelected(currentItem);
+ }
+ });
+ holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (listener != null) listener.onSuggestionItemLongClick(currentItem);
+ return true;
+ }
+ });
}
- private class ViewHolder {
- private final TextView suggestionTitle;
- private ViewHolder(View view) {
- this.suggestionTitle = view.findViewById(android.R.id.text1);
+ private SuggestionItem getItem(int position) {
+ return items.get(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ public boolean isEmpty() {
+ return getItemCount() == 0;
+ }
+
+ public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
+ private final TextView itemSuggestionQuery;
+ private final ImageView suggestionIcon;
+
+ // Cache some ids, as they can potentially be constantly updated/recycled
+ private final int historyResId;
+ private final int searchResId;
+
+ private SuggestionItemHolder(View rootView) {
+ super(rootView);
+ suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
+ itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
+
+ historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history);
+ searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
+ }
+
+ private void updateFrom(SuggestionItem item) {
+ suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId);
+ itemSuggestionQuery.setText(item.query);
+ }
+
+ private static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
+ TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+ int attributeResourceId = a.getResourceId(0, 0);
+ a.recycle();
+ return attributeResourceId;
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 4b0604bb0..f90352fa1 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -178,6 +178,10 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
initExoPlayerCache();
+ if (audioManager == null) {
+ this.audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
+ }
+
AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory);
DefaultLoadControl loadControl = new DefaultLoadControl();
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
index f93134a5d..9a43374a5 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
@@ -7,9 +7,8 @@ import android.support.annotation.Nullable;
import android.support.v7.preference.Preference;
import android.util.Log;
-import com.nononsenseapps.filepicker.FilePickerActivity;
-
import org.schabi.newpipe.R;
+import org.schabi.newpipe.util.FilePickerActivityHelper;
public class DownloadSettingsFragment extends BasePreferenceFragment {
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)) {
- Intent i = new Intent(getActivity(), FilePickerActivity.class)
- .putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
- .putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
- .putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
+ Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
+ .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
+ .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
+ .putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) {
startActivityForResult(i, REQUEST_DOWNLOAD_PATH);
} else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java
index ac70bd05f..c954211fa 100644
--- a/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/AnimationUtils.java
@@ -19,7 +19,7 @@ public class AnimationUtils {
private static final boolean DEBUG = MainActivity.DEBUG;
public enum Type {
- ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA
+ ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA, SLIDE_AND_ALPHA, LIGHT_SLIDE_AND_ALPHA
}
public static void animateView(View view, boolean enterOrExit, long duration) {
@@ -95,9 +95,16 @@ public class AnimationUtils {
case LIGHT_SCALE_AND_ALPHA:
animateLightScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
break;
+ case SLIDE_AND_ALPHA:
+ animateSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
+ break;
+ case LIGHT_SLIDE_AND_ALPHA:
+ animateLightSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
+ break;
}
}
+
/**
* Animate the background color of a view
*/
@@ -237,4 +244,50 @@ public class AnimationUtils {
}).start();
}
}
+
+ private static void animateSlideAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
+ if (enterOrExit) {
+ view.setTranslationY(-view.getHeight());
+ view.setAlpha(0f);
+ view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0)
+ .setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ } else {
+ view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(0f).translationY(-view.getHeight())
+ .setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ }
+ }
+
+ private static void animateLightSlideAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
+ if (enterOrExit) {
+ view.setTranslationY(-view.getHeight() / 2);
+ view.setAlpha(0f);
+ view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0)
+ .setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ } else {
+ view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(0f).translationY(-view.getHeight() / 2)
+ .setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ if (execOnEnd != null) execOnEnd.run();
+ }
+ }).start();
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/Constants.java b/app/src/main/java/org/schabi/newpipe/util/Constants.java
index f9329b0be..b31a95cca 100644
--- a/app/src/main/java/org/schabi/newpipe/util/Constants.java
+++ b/app/src/main/java/org/schabi/newpipe/util/Constants.java
@@ -9,4 +9,6 @@ public class Constants {
public static final String KEY_QUERY = "key_query";
public static final String KEY_THEME_CHANGE = "key_theme_change";
+
+ public static final int NO_SERVICE_ID = -1;
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
index 5cf9f057e..0dd2c00ab 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
@@ -50,7 +50,14 @@ public final class ExtractorHelper {
//no instance
}
+ private static void checkServiceId(int serviceId) {
+ if(serviceId == Constants.NO_SERVICE_ID) {
+ throw new IllegalArgumentException("serviceId is NO_SERVICE_ID");
+ }
+ }
+
public static Single searchFor(final int serviceId, final String query, final int pageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
+ checkServiceId(serviceId);
return Single.fromCallable(new Callable() {
@Override
public SearchResult call() throws Exception {
@@ -61,6 +68,7 @@ public final class ExtractorHelper {
}
public static Single getMoreSearchItems(final int serviceId, final String query, final int nextPageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
+ checkServiceId(serviceId);
return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter)
.map(new Function() {
@Override
@@ -71,6 +79,7 @@ public final class ExtractorHelper {
}
public static Single> suggestionsFor(final int serviceId, final String query, final String searchLanguage) {
+ checkServiceId(serviceId);
return Single.fromCallable(new Callable>() {
@Override
public List call() throws Exception {
@@ -80,6 +89,7 @@ public final class ExtractorHelper {
}
public static Single getStreamInfo(final int serviceId, final String url, boolean forceLoad) {
+ checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable() {
@Override
public StreamInfo call() throws Exception {
@@ -89,6 +99,7 @@ public final class ExtractorHelper {
}
public static Single getChannelInfo(final int serviceId, final String url, boolean forceLoad) {
+ checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable() {
@Override
public ChannelInfo call() throws Exception {
@@ -98,6 +109,7 @@ public final class ExtractorHelper {
}
public static Single getMoreChannelItems(final int serviceId, final String url, final String nextStreamsUrl) {
+ checkServiceId(serviceId);
return Single.fromCallable(new Callable() {
@Override
public NextItemsResult call() throws Exception {
@@ -107,6 +119,7 @@ public final class ExtractorHelper {
}
public static Single getPlaylistInfo(final int serviceId, final String url, boolean forceLoad) {
+ checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable() {
@Override
public PlaylistInfo call() throws Exception {
@@ -116,6 +129,7 @@ public final class ExtractorHelper {
}
public static Single getMorePlaylistItems(final int serviceId, final String url, final String nextStreamsUrl) {
+ checkServiceId(serviceId);
return Single.fromCallable(new Callable() {
@Override
public NextItemsResult call() throws Exception {
@@ -133,6 +147,7 @@ public final class ExtractorHelper {
* and put the results in the cache.
*/
private static Single checkCache(boolean forceLoad, int serviceId, String url, Single loadFromNetwork) {
+ checkServiceId(serviceId);
loadFromNetwork = loadFromNetwork.doOnSuccess(new Consumer() {
@Override
public void accept(@NonNull I i) throws Exception {
@@ -157,6 +172,7 @@ public final class ExtractorHelper {
* Default implementation uses the {@link InfoCache} to get cached results
*/
public static Maybe loadFromCache(final int serviceId, final String url) {
+ checkServiceId(serviceId);
return Maybe.defer(new Callable>() {
@Override
public MaybeSource extends I> call() throws Exception {
diff --git a/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java
new file mode 100644
index 000000000..5f588c5ca
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java
@@ -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);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/util/LayoutManagerSmoothScroller.java b/app/src/main/java/org/schabi/newpipe/util/LayoutManagerSmoothScroller.java
new file mode 100644
index 000000000..9eca2d610
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/LayoutManagerSmoothScroller.java
@@ -0,0 +1,43 @@
+package org.schabi.newpipe.util;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.LinearSmoothScroller;
+import android.support.v7.widget.RecyclerView;
+
+public class LayoutManagerSmoothScroller extends LinearLayoutManager {
+
+ public LayoutManagerSmoothScroller(Context context) {
+ super(context, VERTICAL, false);
+ }
+
+ public LayoutManagerSmoothScroller(Context context, int orientation, boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ }
+
+ @Override
+ public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
+ RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
+ smoothScroller.setTargetPosition(position);
+ startSmoothScroll(smoothScroller);
+ }
+
+ private class TopSnappedSmoothScroller extends LinearSmoothScroller {
+ public TopSnappedSmoothScroller(Context context) {
+ super(context);
+
+ }
+
+ @Override
+ public PointF computeScrollVectorForPosition(int targetPosition) {
+ return LayoutManagerSmoothScroller.this
+ .computeScrollVectorForPosition(targetPosition);
+ }
+
+ @Override
+ protected int getVerticalSnapPreference() {
+ return SNAP_TO_START;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 0a529ab4e..538675685 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -14,7 +14,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.about.AboutActivity;
import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe;
+import org.schabi.newpipe.extractor.ServiceList;
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.StreamInfo;
import org.schabi.newpipe.fragments.MainFragment;
@@ -228,13 +230,17 @@ public class NavigationHelper {
// Link handling
//////////////////////////////////////////////////////////////////////////*/
- public static void openByLink(Context context, String url) throws Exception {
- Intent intentByLink = getIntentByLink(context, url);
- if (intentByLink == null)
- throw new NullPointerException("getIntentByLink(context = [" + context + "], url = [" + url + "]) returned null");
+ public static boolean openByLink(Context context, String url) {
+ Intent intentByLink;
+ try {
+ intentByLink = getIntentByLink(context, url);
+ } catch (ExtractionException e) {
+ return false;
+ }
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentByLink);
+ return true;
}
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
@@ -245,14 +251,20 @@ public class NavigationHelper {
return mIntent;
}
- private static Intent getIntentByLink(Context context, String url) throws Exception {
- StreamingService service = NewPipe.getServiceByUrl(url);
+ public static Intent getIntentByLink(Context context, String url) throws ExtractionException {
+ 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();
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
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);
@@ -268,7 +280,7 @@ public class NavigationHelper {
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) {
case STREAM:
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);
diff --git a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java
index bd268abf7..51dceddf3 100644
--- a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java
+++ b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java
@@ -21,6 +21,7 @@ package org.schabi.newpipe.util;
import android.content.Context;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +30,7 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
+import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity;
import java.io.File;
@@ -110,6 +112,7 @@ public class StateSaver {
/**
* Try to restore the state from memory and disk, using the {@link StateSaver.WriteRead#readFrom(Queue)} from the writeRead.
*/
+ @Nullable
private static SavedState tryToRestore(@NonNull SavedState savedState, @NonNull WriteRead writeRead) {
if (MainActivity.DEBUG) {
Log.d(TAG, "tryToRestore() called with: savedState = [" + savedState + "], writeRead = [" + writeRead + "]");
@@ -117,7 +120,7 @@ public class StateSaver {
FileInputStream fileInputStream = null;
try {
- Queue savedObjects = stateObjectsHolder.remove(savedState.prefixFileSaved);
+ Queue savedObjects = stateObjectsHolder.remove(savedState.getPrefixFileSaved());
if (savedObjects != null) {
writeRead.readFrom(savedObjects);
if (MainActivity.DEBUG) {
@@ -126,8 +129,13 @@ public class StateSaver {
return savedState;
}
- File file = new File(savedState.pathFileSaved);
- if (!file.exists()) return null;
+ File file = new File(savedState.getPathFileSaved());
+ if (!file.exists()) {
+ if(MainActivity.DEBUG) {
+ Log.d(TAG, "Cache file doesn't exist: " + file.getAbsolutePath());
+ }
+ return null;
+ }
fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
@@ -139,7 +147,7 @@ public class StateSaver {
return savedState;
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to restore state", e);
} finally {
if (fileInputStream != null) {
try {
@@ -154,10 +162,17 @@ public class StateSaver {
/**
* @see #tryToSave(boolean, String, String, WriteRead)
*/
+ @Nullable
public static SavedState tryToSave(boolean isChangingConfig, @Nullable SavedState savedState, Bundle outState, WriteRead writeRead) {
- String currentSavedPrefix = savedState == null || TextUtils.isEmpty(savedState.prefixFileSaved)
- ? System.nanoTime() - writeRead.hashCode() + ""
- : savedState.prefixFileSaved;
+ @NonNull
+ String currentSavedPrefix;
+ if (savedState == null || TextUtils.isEmpty(savedState.getPrefixFileSaved())) {
+ // Generate unique prefix
+ currentSavedPrefix = System.nanoTime() - writeRead.hashCode() + "";
+ } else {
+ // Reuse prefix
+ currentSavedPrefix = savedState.getPrefixFileSaved();
+ }
savedState = tryToSave(isChangingConfig, currentSavedPrefix, writeRead.generateSuffix(), writeRead);
if (savedState != null) {
@@ -173,22 +188,33 @@ public class StateSaver {
* to the file with the name of prefixFileName + suffixFileName, in a cache folder got from the {@link #init(Context)}.
*
* It checks if the file already exists and if it does, just return the path, so a good way to save is:
- *
A fixed prefix for the file
- * A changing suffix
+ *
+ * A fixed prefix for the file
+ * A changing suffix
+ *
+ *
+ * @param isChangingConfig
+ * @param prefixFileName
+ * @param suffixFileName
+ * @param writeRead
*/
+ @Nullable
private static SavedState tryToSave(boolean isChangingConfig, final String prefixFileName, String suffixFileName, WriteRead writeRead) {
if (MainActivity.DEBUG) {
Log.d(TAG, "tryToSave() called with: isChangingConfig = [" + isChangingConfig + "], prefixFileName = [" + prefixFileName + "], suffixFileName = [" + suffixFileName + "], writeRead = [" + writeRead + "]");
}
- Queue savedObjects = new LinkedList<>();
+ LinkedList savedObjects = new LinkedList<>();
writeRead.writeTo(savedObjects);
if (isChangingConfig) {
if (savedObjects.size() > 0) {
stateObjectsHolder.put(prefixFileName, savedObjects);
return new SavedState(prefixFileName, "");
- } else return null;
+ } else {
+ if(MainActivity.DEBUG) Log.d(TAG, "Nothing to save");
+ return null;
+ }
}
FileOutputStream fileOutputStream = null;
@@ -197,8 +223,12 @@ public class StateSaver {
if (!cacheDir.exists()) throw new RuntimeException("Cache dir does not exist > " + cacheDirPath);
cacheDir = new File(cacheDir, CACHE_DIR_NAME);
if (!cacheDir.exists()) {
- boolean mkdirResult = cacheDir.mkdir();
- if (!mkdirResult) return null;
+ if(!cacheDir.mkdir()) {
+ if(BuildConfig.DEBUG) {
+ Log.e(TAG, "Failed to create cache directory " + cacheDir.getAbsolutePath());
+ }
+ return null;
+ }
}
if (TextUtils.isEmpty(suffixFileName)) suffixFileName = ".cache";
@@ -214,7 +244,9 @@ public class StateSaver {
return name.contains(prefixFileName);
}
});
- for (File file1 : files) file1.delete();
+ for (File fileToDelete : files) {
+ fileToDelete.delete();
+ }
}
fileOutputStream = new FileOutputStream(file);
@@ -223,7 +255,7 @@ public class StateSaver {
return new SavedState(prefixFileName, file.getAbsolutePath());
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to save state", e);
} finally {
if (fileOutputStream != null) {
try {
@@ -241,11 +273,11 @@ public class StateSaver {
public static void onDestroy(SavedState savedState) {
if (MainActivity.DEBUG) Log.d(TAG, "onDestroy() called with: savedState = [" + savedState + "]");
- if (savedState != null && !TextUtils.isEmpty(savedState.pathFileSaved)) {
- stateObjectsHolder.remove(savedState.prefixFileSaved);
+ if (savedState != null && !TextUtils.isEmpty(savedState.getPathFileSaved())) {
+ stateObjectsHolder.remove(savedState.getPrefixFileSaved());
try {
//noinspection ResultOfMethodCallIgnored
- new File(savedState.pathFileSaved).delete();
+ new File(savedState.getPathFileSaved()).delete();
} catch (Exception ignored) {
}
}
@@ -271,9 +303,12 @@ public class StateSaver {
// Inner
//////////////////////////////////////////////////////////////////////////*/
+ /**
+ * Information about the saved state on the disk
+ */
public static class SavedState implements Parcelable {
- public String prefixFileSaved;
- public String pathFileSaved;
+ private final String prefixFileSaved;
+ private final String pathFileSaved;
public SavedState(String prefixFileSaved, String pathFileSaved) {
this.prefixFileSaved = prefixFileSaved;
@@ -287,7 +322,7 @@ public class StateSaver {
@Override
public String toString() {
- return prefixFileSaved + " > " + pathFileSaved;
+ return getPrefixFileSaved() + " > " + getPathFileSaved();
}
@Override
@@ -313,6 +348,22 @@ public class StateSaver {
return new SavedState[size];
}
};
+
+ /**
+ * Get the prefix of the saved file
+ * @return the file prefix
+ */
+ public String getPrefixFileSaved() {
+ return prefixFileSaved;
+ }
+
+ /**
+ * Get the path to the saved file
+ * @return the path to the saved file
+ */
+ public String getPathFileSaved() {
+ return pathFileSaved;
+ }
}
diff --git a/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java b/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
index 479a5cee3..acbd41680 100755
--- a/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
+++ b/app/src/main/java/us/shandian/giga/get/DownloadManagerImpl.java
@@ -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 missions) {
+ Collections.sort(missions, new Comparator() {
+ @Override
+ public int compare(DownloadMission o1, DownloadMission o2) {
+ return Long.valueOf(o1.timestamp).compareTo(o2.timestamp);
+ }
+ });
+ }
/**
* Loads finished missions from the data source
@@ -111,12 +123,8 @@ public class DownloadManagerImpl implements DownloadManager {
finishedMissions = new ArrayList<>();
}
// Ensure its sorted
- Collections.sort(finishedMissions, new Comparator() {
- @Override
- public int compare(DownloadMission o1, DownloadMission o2) {
- return (int) (o1.timestamp - o2.timestamp);
- }
- });
+ sortByTimestamp(finishedMissions);
+
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
for (DownloadMission mission : finishedMissions) {
File downloadedFile = mission.getDownloadedFile();
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index 9ba44e76f..04b10347c 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -51,6 +51,25 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbar_search_layout.xml b/app/src/main/res/layout/toolbar_search_layout.xml
index 7780a0226..797eea48e 100644
--- a/app/src/main/res/layout/toolbar_search_layout.xml
+++ b/app/src/main/res/layout/toolbar_search_layout.xml
@@ -4,16 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
- android:background="?attr/colorPrimary"
- android:focusable="true"
- android:focusableInTouchMode="true">
+ android:background="?attr/colorPrimary">
-
-
-
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..a95153a1f
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..bccfaff0e
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..65ced7a8b
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..b7b42cbff
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..4ce448946
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 58a13dd09..c3bd768df 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -2,7 +2,7 @@
Calca na gueta pa entamar
%1$s visiones
- Espublizáu\'l %1$s
+ Espublizóse\'l %1$s
Nun s\'alcontró un reproductor de fluxos. ¿Quies instalar VLC?
Instalar
Encaboxar
@@ -14,15 +14,15 @@
¿Quixesti dicir %1$s?
Compartir con
Escoyer restolador
- rotación
- Usar reproductor de videu esternu
- Usar reproductor d\'audiu esternu
+ voltéu
+ Usar reproductor esternu de videu
+ Usar reproductor esternu d\'audiu
Camín de descarga de vídeos
- Camín nel qu\'atroxar los vídeos baxaos.
+ Camín nel qu\'atroxar los vídeos baxaos
Introducir camín de descarga pa vídeos
- Camín de descarga d\'audiu
+ Camín de descarga p\'audios
Camín nel qu\'atroxar los audios baxaos.
Introducir camín de descarga pa ficheros d\'audiu
@@ -30,11 +30,11 @@
Reproducir con Kodi
Nun s\'alcontró Kore. ¿Instalalu?
Amosar opción «Reproducir con Kodi»
- Amuesa una opción pa reproducir un videu per Kodi.
+ Amuesa una opción pa reproducir un videu per Kodi
Audiu
- Formatu d\'audiu por defeutu
+ Formatu por defeutu d\'audiu
WebM — formatu llibre
- m4a — calidá meyor
+ M4A — calidá meyor
Tema
Escuru
Claru
@@ -43,15 +43,15 @@
Videu siguiente
Amosar vídeos siguientes y asemeyaos
URL non sofitada
- Llingua de conteníu preferíu
+ Llingua por defeutu del conteníu
Videu y audiu
Aspeutu
Otru
Reproduciendo de fondu
Reproducir
Conteníu
- Amosar conteníu restrinxíu pola edá
- El videu ta restrinxíu pola edá. Deshabilita esto diendo primero a axustes.
+ Amosar conteníu torgáu pola edá
+ El videu ta torgáu pola edá. Desactiva esto diendo primero a axustes.
en direuto
Descargues
Descargues
@@ -60,16 +60,16 @@
Fallu
Fallu de rede
Nun pudieron cargase toles miniatures
- Nun pudo descargase la robla de la url del videu.
- Nun pudo analizase\'l sitiu web.
- Nun pudo analizase dafechu\'l sitiu web.
- Conteníu non disponible.
- Bloquiáu por GEMA.
- Nun pudo configurase\'l menú de descarga.
- Esto ye una tresmisión de direuto pero entá nun ta sofitao.
- Nun pudo consiguise tresmisión dala.
+ Nun pudo descifrase la robla de la URL
+ Nun pudo analizase\'l sitiu web
+ Nun pudo analizase dafechu\'l sitiu web
+ Conteníu non disponible
+ Bloquiáu por GEMA
+ Nun pudo configurase\'l menú de descarga
+ Esto ye una tresmisión de direuto qu\'entá nun se sofita.
+ Nun pudo consiguise tresmisión dala
Perdón, eso nun debió asoceder.
- Fallu d\'informe per corréu
+ Informar per corréu del fallu
Perdón, asocedieron dellos fallos.
INFORMAR
Información:
@@ -78,11 +78,11 @@
Detalles:
- Miniatura de previsualización de videu
- Miniatura de previsualización de videu
+ Miniatura de previsualización del videu
+ Miniatura de previsualización del videu
Préstames
Usar Tor
- (Esperimental) Forcia\'l tráficu de descargues pente Tor pa más privacidá (la tresmisión de vídeos entá nun ta sofitao).
+ (Esperimental) Forcia\'l tráficu de descargues pente Tor pa más privacidá (la tresmisión de vídeos entá nun se sofita).
Informa d\'un fallu
Informe d\'usuariu
@@ -92,11 +92,11 @@
Videu
Audiu
Retentar
- Ñegóse l\'accesu al almacenamientu
+ Ñegóse\'l permisu d\'accesu al almacenamientu
Aniciar
Posar
- Ver
+ Reproducir
Desaniciar
Suma de comprobación
@@ -110,23 +110,23 @@
URL malformada o internet non disponible
Calca pa detallles
Espera, por favor…
- Copióse al cartafueyu.
- Esbilla un direutoriu de descarga disponible, por favor.
+ Copióse al cartafueyu
+ Esbilla una carpeta disponible de descarga, por favor
- Auto-reproduz un videu al llamar a NewPipe dende otra aplicación.
- Auto-reproducir al llamar dende otra aplicación
- Miniatura del xubidor
+ Auto-reproduz un videu al llamar a NewPipe dende otra aplicación
+ Auto-reproducción
+ Miniatura del avatar del xubidor
Despréstames
NewPipe baxando
Nun pudo cargase la imaxe
Cascó l\'aplicación/IU
-
+ Lo qu\'asocedió:\\nSolicitú:\\nLlingua del conteníu:\\nServiciu:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del SO:\\nRangu global d\'IP:
Abrir en ventanu emerxente
Mou de ventanu emerxente de NewPipe
- Formatu preferíu de videu
+ Formatu por defeutu de videu
Prietu
Reproduciendo en ventanu emerxente
@@ -134,7 +134,7 @@
Canal
Sí
Más sero
- Deshabilitóse
+ Desactivóse
Usar reproductor vieyu
@@ -142,26 +142,26 @@
M
Mill
MMill
- Precísase esti permisu pa
-abrir en ventanu emerxente
+ Precísase esti permisu p\'abrir
+\nen ventanu emerxente
reCAPTCHA
Prueba reCAPTCHA
- Prueba reCAPTCHA solicitada
+ Solicitóse la prueba reCAPTCHA
Fondu
Ventanu emerxente
- Resolución por defeutu de ventanu emerxente
+ Resolución por defeutu del ventanu emerxente
Amosar resoluciones más altes
- Namái dellos preseos sofiten vídeos en 2k/4k
+ Namái dellos preseos sofiten vídeos en 2K/4K
Peñera
Refrescar
Llimpiar
- Delles resoluciones NUN tendrán audiu al habilitar esta opción
- Tamañu y posición del ventanu emerxente
- Recuerda la cabera posición y resolución afitada nel ventanu emerxente
+ Delles resoluciones NUN tendrán audiu al activar esta opción
+ Recuerdar tamañu y posición del ventanu emerxente
+ Recuerda la cabera posición y resolución afitaes nel ventanu emerxente
Controles per xestos del reproductor
Usa xestos pa controlar el brilléu y volume del reproductor
Suxerencies de gueta
@@ -170,5 +170,87 @@ abrir en ventanu emerxente
Ventanu emerxente
Redimensionáu
- Compilación vieya del reproductor Mediaframework.
+ Reproductor vieyu integráu de Mediaframework
+Soscribise
+ Soscribiéstite
+ Desoscribiéstite de la canal
+ Nun pue camudase la resolución
+ Nun pue anovase la soscripción
+
+ Principal
+ Soscripciones
+
+ Qué hai nuevo
+
+ Historial de gueta
+ Atroxa de mou llocal les solicitúes de gueta
+ Historial
+ Fai un siguimientu de los vídeos vistos
+ Siguir al ganar el focu
+ Sigue reproduciendo tres les interrupciones (por exemplu, llamaes de teléfonu)
+ Reproductor
+ Comportamientu
+ Historial
+ Llistáu de reproducción
+ La meyor resolución
+ Desfacer
+
+ Avisu de NewPipe
+ Avisos pa los reproductores de fondu y en ventanu emerxente de NewPipe
+
+ Ensin resultaos
+ Equí nun hai más que grillos
+
+ Ensin soscriptores
+
+ - %s soscriptor
+ - %s soscriptores
+
+
+ Ensin visionaos
+
+ - %s visionáu
+ - %s visionaos
+
+
+ Nun hai vídeos
+
+ - %s videu
+ - %s vídeos
+
+
+ Descargues
+ Caráuteres permitíos nos nomes de ficheros
+ Los caráuteres non válidos tróquense por esti valor
+ Troquéu de caráuteres
+
+ Lletres y díxitos
+ La mayoría de caráuteres especiales
+
+ Tocante a NewPipe
+ Axustes
+ Tocante a
+ Llicencies de terceros
+ © %1$s por %2$s so la %3$s
+ Nun pudo cargase la llicencia
+ Abrir sitiu web
+ Tocante a
+ Collaboradores
+ Llicencies
+ Un frontal llixeru de YouTube p\'Android.
+ Ver en GitHub
+ Llicencia de NewPipe
+ Si tienes idees, quies traducir, facer cambeos, llimpiar el códigu u otres coses, l\'ayuda siempres s\'agradez. ¡Cuánto más se faiga, más s\'ameyora!
+ Lleer llicencia
+ Collaboración
+
+ Historial
+ Guetao
+ Visto
+ L\'historial ta desactiváu
+ Historial
+ L\'historial ta baleru
+ Llimpióse l\'historial
+ Desanicióse l\'elementu
+ ¿Quies desaniciar esti elementu del historial de gueta?
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d863d8114..0e183f2f8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -119,7 +119,7 @@
Starten
Pause
- Ansehen
+ Abspielen
Neue Mission
OK
Server nicht unterstützt
@@ -254,8 +254,9 @@
Die meisten Sonderzeichen
Element gelöscht
-Fortsetzen beim erneuten fokussieren
+ Fortsetzen bei erneutem Fokussieren
Player
Nichts hier außer Grillen
-
+ Möchten Sie dieses Element aus dem Suchverlauf löschen?
+
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index b05b30a75..7d09d85b9 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -72,7 +72,7 @@
Mostrar contenido restringido por edad
Vídeo restringido por edad. Permitir este tipo de material es posible desde Ajustes.
- Toque buscar para empezar
+ Toque en buscar para empezar
Autoreproducir
Reproducir automáticamente un vídeo cuando NewPipe es llamado desde otra aplicación
en vivo
@@ -259,4 +259,5 @@ abrir en modo popup
Elemento eliminado
+¿Desea eliminar este elemento del historial de búsqueda?
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 063afe594..f8de02050 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -18,7 +18,7 @@
Paramètres
Partager
Partager avec
- Afficher une option pour lire la vidéo via la médiathèque Kodi
+ Afficher une option pour lire la vidéo via Kodi
Afficher l’option « Lire avec Kodi »
Ajoutée le %1$s
%1$s vues
@@ -256,4 +256,5 @@
Caractères spéciaux
Objet effacé
+Voulez-vous supprimer cet élément de l\'historique de recherche ?
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 39084cecb..bac01e17f 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -23,7 +23,7 @@
Lokasi untuk menyimpan audio yang diunduh
Masukkan lokasi unduhan berkas audio
- Putar otomatis ketika dipanggil dari aplikasi lain
+ Putar otomatis
Otomatis memutar video ketika NewPipe dijalankan dari aplikasi lain
Resolusi baku
Putar dengan Kodi
@@ -33,7 +33,7 @@
Audio
Format audio baku
WebM — format bebas
- m4a — kualitas lebih baik
+ M4A — kualitas lebih baik
Tema
Gelap
Terang
@@ -61,15 +61,15 @@
Laporan galat
Galat
- Tidak bisa mengurai situs web.
- Sama sekali tidak bisa mengurai situs web.
- Konten tidak tersedia.
- Diblokir oleh GEMA.
- Tidak bisa menyiapkan menu unduhan.
- Ini adalah SIARAN LANGSUNG. Fitur ini belum didukung.
+ Tidak dapat mengurai situs web
+ Sama sekali tidak dapat mengurai situs web
+ Konten tidak tersedia
+ Diblokir oleh GEMA
+ Tidak bisa menyiapkan menu unduhan
+ Ini adalah SIARAN LANGSUNG, yang mana ini belum didukung.
Tidak bisa memuat gambar
Maaf, hal tersebut seharusnya tidak terjadi.
- Lapor galat via surel
+ Lapor galat via surat elektronik
Maaf, telah terjadi galat.
LAPOR
Info:
@@ -92,7 +92,7 @@
Video
Audio
Ulangi
- Izin untuk mengakses penyimpanan ditolak
+ Izin akses penyimpanan ditolak
Hapus
Tonton
@@ -117,7 +117,7 @@
Tidak ditemukan pemutar stream. Apakah anda ingin memasang VLC?
Tidak bisa mendekrip tanda tangan URL video.
App/UI rusak
- Tidak bisa mendapatkan stream apapun.
+ Tidak bisa mendapatkan stream apapun
Apa:\\nPermintaan:\\nBahasa Konten:\\nLayanan:\\nWaktu GMT:\\nPaket:\\nVersi:\\nVersi OS:\\nIP:
Laporan pengguna
@@ -145,11 +145,11 @@
Izin ini dibutuhkan untuk
membuka di mode popup
- Mode Popup NewPipe
+ Mode popup NewPipe
Memutar dalam mode popup
Gunakan pemutar lama
- Versi lama pemutar Mediaframework.
+ Versi lama pemutar Mediaframework
Dinonaktifkan
Pilihan format video
@@ -166,7 +166,7 @@ membuka di mode popup
Filter
Beberapa resolusi TIDAK akan memiliki suara ketika opsi ini diaktifkan
Ingat ukuran dan posisi sembulan
- Ingat ukuran terakhir dan pengaturan posisi sembulan
+ Ingat ukuran terakhir dan pengaturan posisi popup
Sembulan
Ubah ukuran
@@ -212,7 +212,7 @@ membuka di mode popup
Utama
Cari riwayat
Simpan pencarian secara lokal
- Riwayat tontonan
+ Riwayat
Notifikasi NewPipe
Riwayat
Riwayat dinonaktifkan
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 30530c73d..0081c9844 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -12,16 +12,16 @@
Impostazioni
Intendevi: %1$s ?
Condividi con
- Scegli browser
+ Scegli il browser
rotazione
- Cartella dei video scaricati
- Cartella in cui memorizzare i video scaricati.
+ Percorso dei video scaricati
+ Percorso in cui memorizzare i video scaricati
Inserisci il percorso per i download
Risoluzione predefinita
Riproduci con Kodi
- L\'applicazione Kore non è stata trovata. Vorresti installarla?
+ L\'applicazione Kore non è stata trovata. Vuoi installarla?
Mostra l\'opzione \"Riproduci con Kodi\"
- Mostra l\'opzione per riprodurre i video tramite Kodi.
+ Mostra l\'opzione per riprodurre i video tramite Kodi
Audio
Formato audio predefinito
WebM — formato libero
@@ -30,7 +30,7 @@
Prossimo video
Mostra video a seguire e video simili
URL non supportato
- Lingua preferita per i contenuti
+ Lingua predefinita per i contenuti
Video e Audio
Anteprima video
@@ -40,12 +40,12 @@
Mi piace
Impossibile creare la cartella di download \'%1$s\'
Creata la cartella per i download \'%1$s\'
- Usa un lettore video esterno
- Usa un lettore audio esterno
+ Usa un riproduttore video esterno
+ Usa un riproduttore audio esterno
- Cartella degli audio scaricati
- Cartella dove salvare gli audio scaricati.
- Inserisci la cartella per i file audio
+ Percorso degli audio scaricati
+ Percorso dove salvare gli audio scaricati
+ Inserisci il percorso per i file audio
Tema
Scuro
@@ -63,26 +63,26 @@
Contenuto non disponibile
Bloccato dalla GEMA
Usa Tor
- (Sperimentale) Forza il traffico in download tramite Tor per una maggiore privacy (lo streaming dei video non è ancora supportato).
+ (Sperimentale) Forza il traffico in download tramite Tor per una maggiore riservatezza (lo streaming dei video non è ancora supportato).
Impossibile analizzare il sito web
Impossibile impostare il menù di download
- Questo è uno stream dal vivo. Gli stream dal vivo non sono ancora supportati.
+ Questo è uno stream in diretta, il quale non è ancora supportato.
Contenuti
Mostra contenuti vincolati all\'età
- Questo video è riservato ad un pubblico maturo. Per accedervi, abilita \"Mostra video vincolati all\'età\" nelle impostazioni.
+ Questo video è riservato ad un pubblico maggiorenne. Per accedervi, abilita \"Mostra video vincolati all\'età\" nelle impostazioni.
Tocca \"cerca\" per iniziare
- Inizia automaticamente la riproduzione se NewPipe viene aperto da un\'altra app
+ Riproduzione automatica
Riproduci i video automaticamente quando NewPipe viene aperto da un\'altra app
in diretta
- Impossibile eseguire il parsing completo del sito
- Non è stato ottenuto alcuno stream
+ Impossibile analizzare completamente il sito web
+ Non è stato ottenuto alcun flusso
Ci dispiace, non sarebbe dovuto succedere.
Segnala l\'errore via e-mail
Ci dispiace, c\'è stato qualche errore.
@@ -99,18 +99,18 @@
Video
Audio
Riprova
- È stato negato il permesso di accedere all\'archiviazione di massa
+ È stato negato il permesso di accesso all\'archiviazione di massa
Download
Download
Segnalazione errori
Inizia
Pausa
- Visualizza
+ Riproduci
Elimina
- Checksum
+ Codice di controllo
- Nuova missione
+ Nuovo obiettivo
OK
Nome del file
@@ -126,7 +126,7 @@
Seleziona una cartella disponibile in cui salvare i download
Impossibile caricare l\'immagine
- L\'app/UI è andata in crash
+ L\'app/UI si è interrotta
Cosa:\\nRichiesta:\\nLingua contenuto:\\nServizio:\\nOrario GMT:\\nPacchetto:\\nVersione:\\nVersione SO:\\nRange IP glob.:
reCAPTCHA
@@ -149,26 +149,26 @@
Apri in modalità popup
- NewPipe Modo popup
+ NewPipe in modalità popup
Riproduzione in modalità popup
Disattivato
Usa il vecchio riproduttore
-Alcune risoluzioni non avranno audio se questa opzione viene abilitata.
+Alcune risoluzioni NON avranno l\'audio se questa opzione viene abilitata
In sottofondo
Popup
- Risoluzione predefinita per il popup
+ Risoluzione predefinita per la modalità popup
Mostra risoluzioni più alte
- Solo alcuni dispositivi supportano la riproduzione di video in 2K e 4K.
- Formato video preferito
+ Solo alcuni dispositivi supportano la riproduzione di video in 2K e 4K
+ Formato video predefinito
Ricorda grandezza e posizione del popup
Ricorda l\'ultima grandezza e posizione del popup
Controlli gestuali
- Usa i gesti per controllare luminosità e volume.
+ Usa i gesti per controllare luminosità e volume
Suggerimenti di ricerca
- Mostra suggerimenti durante la ricerca.
+ Mostra i suggerimenti durante la ricerca
Popup
Filtra i risultati
@@ -177,10 +177,10 @@
Ridimensionamento
Risoluzione migliore
- Precedente riproduttore integrato facente uso di Mediaframework
+ Precedente riproduttore integrato Mediaframework
- Questo permesso è necessario
-\nper la modalità popup
+ Questo permesso è necessario
+\nper aprire la modalità popup
Impostazioni
Informazioni
@@ -206,7 +206,7 @@
Iscrizioni
- Nuovo
+ Novità
Cronologia ricerche
Salva le ricerche localmente
@@ -233,10 +233,10 @@
Cronologia cancellata
Principale
- Player
+ Riproduttore
Comportamento
Cronologia
- Playlist
+ Scaletta
Annulla
Notifiche NewPipe
@@ -262,4 +262,7 @@
Elemento eliminato
+Nulla da mostrare
+
+ Vuoi eliminare questo elemento dalla cronologia?
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 02fa7cacf..ae4d6e10e 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -253,4 +253,5 @@
Historikken er tom
Historikk tømt
Element slettet
+Ønsker du å slette dette elementet fra søkehistorikken?
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 200c02810..75cebf954 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -104,7 +104,7 @@
Toegang tot opslag geweigerd
Begin
Pauzeren
- Bekijken
+ Afspelen
Verwijderen
Controlesom
@@ -260,4 +260,5 @@ te openen in pop-upmodus
Item verwijderd
+Wil je dit item uit je geschiedenis verwijderen?
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 3340d24a6..9123f868c 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -7,7 +7,7 @@
Informações:
WebM — formato aberto
%1$s visualizações
- Ver
+ Reproduzir
Vídeo com restrição de idade. Permissão para vídeos com essa restrição podem ser feitas no menu configurações.
Vídeo
Reproduzir o vídeo automaticamente quando o NewPipe for aberto a partir de outro app
@@ -238,7 +238,8 @@ abrir em modo popup
Item excluído
-Player
+Reprodutor
Não há nada aqui
-
+ Deseja apagar este item do seu histórico de busca?
+
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index c9e138d1e..bb040c28f 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -21,18 +21,18 @@
Introduza o caminho para os vídeos
Resolução padrão
Reproduzir no Kodi
- Aplicação não encontrada. Instalar o Kore?
+ Aplicação Kore não encontrada. Quer instalá-la?
Mostrar opção \"Reproduzir no Kodi\"
Mostra uma opção para reproduzir o vídeo no Kodi
Áudio
Formato áudio padrão
WebM — formato livre
- m4a — melhor qualidade
+ M4A — melhor qualidade
Transferir
Vídeo seguinte
Mostrar vídeos seguintes e semelhantes
URL não suportado
- Idioma preferido do conteúdo
+ Idioma predefinido do conteúdo
Vídeo e áudio
Miniatura de vídeos
@@ -60,19 +60,19 @@
Diretório \'%1$s\' criado com sucesso
Erro
Incapaz de carregar todas as miniaturas
- Incapaz de decodificar a assinatura do vídeo.
- Incapaz de processar o sítio web.
- Conteúdo não disponível.
- Bloqueado pela GEMA.
+ Incapaz de decodificar a assinatura do vídeo
+ Incapaz de processar o site
+ Conteúdo não disponível
+ Bloqueado pela GEMA
Conteúdo
Restringir conteúdo por idade
- O vídeo está restrito por idade. Ative a restrição de vídeos por idade nas definições.
+ Vídeo com restrição de idade. É possível permitir este material através das Definições.
- Não foi possível processar o sítio web.
- Não foi possível configurar o menu de transferências.
- Esta é uma EMISSÃO EM DIRETO. Estas emissões ainda não são suportadas.
- Não foi possível obter a emissão.
+ Não foi possível processar totalmente o site
+ Não foi possível configurar o menu de transferências
+ Esta é uma EMISSÃO EM DIRETO, as quais ainda não são suportadas.
+ Não foi possível obter a emissão
Desculpe, isto não deveria ter acontecido.
Reportar erro por e-mail
Ocorreram alguns erros.
@@ -86,9 +86,9 @@
Vídeo
Áudio
Tentar novamente
- Não foi concedida permissão para aceder ao armazenamento
+ Permissão para aceder ao armazenamento foi negada
Toque para iniciar a pesquisa
- Reproduzir se invocado por outra aplicação
+ Reprodução automática
Reproduzir vídeo automaticamente se o NewPipe for invocado por outra aplicação
direto
@@ -101,7 +101,7 @@
Iniciar
Pausa
- Ver
+ Reproduzir
Apagar
Checksum
@@ -115,8 +115,8 @@
URL inválido ou Internet não disponível
Toque para detalhes
Por favor aguarde…
- Copiado para a área de transferência.
- Por favor selecione um diretório disponível.
+ Copiado para a área de transferência
+ Por favor selecione a pasta para as descargas
OK
Processos
@@ -145,11 +145,11 @@ o modo “popup“
Desafio reCAPTCHA
Desafio reCAPTCHA solicitado
- Modo popup de NewPipe
+ Modo popup do NewPipe
Reproduzir em modo de popup
Usar reprodutor antigo
- Versão antiga no reprodutor Mediaframework.
+ Versão antiga do reprodutor Mediaframework
Formato de vídeo preferido
Desativado
@@ -186,10 +186,74 @@ o modo “popup“
Sobre
Colaboradores
Licenças
- Aplicação leve, simples e grátis de Youtube para Android.
- Ver no Github
+ Aplicação leve, simples e grátis de YouTube para Android.
+ Ver no GitHub
Licença do NewPipe
- Se tem ideias, tradução, alterações de design, limpeza de código ou alterações de código pesado, ajuda é sempre bem-vinda. Quanto mais se faz melhor fica!
+ Se tem ideias de tradução, alterações de design, limpeza de código ou alterações de código pesado—ajuda é sempre bem-vinda. Quanto mais se faz melhor fica!
Ler licença
Contribuição
+Subscrever
+ Subscrito
+ Canal não subscrito
+ Incapaz de alterar a subscrição
+ Incapaz de atualizar a subscrição
+
+ Principal
+ Subscrições
+
+ O que há de novo
+
+ Histórico de Pesquisa
+ Armazenar termos de pesquisa localmente
+ Histórico
+ Armazenar histórico de vídeos assistidos
+ Retomar ao ganhar foco
+ Continuar reprodução após interrupções (ex. chamadas)
+ Reprodutor
+ Comportamento
+ Histórico
+ Lista de Reprodução
+ Desfazer
+
+ Notificação do NewPipe
+ Notificações do NewPipe e para reprodutores pop-up
+
+ Sem resultados
+ Aqui não há nada para ver
+
+ Sem subscritores
+
+ - %s subscrito
+ - %s subscritos
+
+
+ Sem visualizações
+
+ - %s visualização
+ - %s visualizações
+
+
+ Sem vídeos
+
+ - %s vídeo
+ - %s vídeos
+
+
+ Download
+ Caracteres permitidos em nomes de ficheiros
+ Caracteres inválidos são substituídos por este valor
+ Carácter de substituição
+
+ Letras e dígitos
+ Caracteres especiais
+
+ Histórico
+ Pesquisado
+ Visto
+ Histórico está desativado
+ Histórico
+ O histórico está vazio
+ Histórico eliminado
+ Item apagado
+Deseja apagar este item do histórico de pesquisa?
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 115f7eaf7..e60ec9917 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -96,10 +96,10 @@
Это прямая трансляция, они пока не поддерживаются.
Не удалось загрузить изображение
"Падение приложения/пользовательского интерфейса "
- Простите, такое не должно было произойти.
+ Простите, это не должно было произойти.
Отправить отчёт об ошибке по электронной почте
Простите, произошли ошибки.
- ОТЧЕТ
+ ОТЧЁТ
Информация:
Что произошло:
Детали:
@@ -237,7 +237,7 @@
- %s подписчик
- %s подписчика
- %s подписчиков
-
+
Нет просмотров
@@ -245,7 +245,7 @@
- %s просмотр
- %s просмотра
- %s просмотров
-
+
Нет видео
@@ -253,7 +253,9 @@
- %s видео
- %s видео
- %s видео
-
+
-
+ Элемент удалён
+Удалить этот элемент из истории поиска?
+
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index 3bd31c4c5..798063738 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -102,7 +102,7 @@
Začnite z iskanjem
Začni
Premor
- Poglej
+ Predvajaj
Izbriši
Nadzorna vsota
@@ -272,4 +272,5 @@ odpiranje v pojavnem načinu
Predmet je izbrisan
+Ali želite izbrisati predmet iz zgodovine iskanja?
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 26fc272ff..9bb47a7c7 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -100,7 +100,7 @@
Аутоматско пуштање видеа по позиву друге апликације
Почни
Паузирај
- Приказ
+ Пусти
Обриши
Хеш
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index fbedb9cc6..d56ff5e11 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -97,7 +97,7 @@
Başlat
Duraklat
- Görünüm
+ Oynat
Sil
Sağlama
@@ -255,4 +255,5 @@
Geçmiş boş
Geçmiş temizlendi
Öge silindi
+Bu içeriği arama geçmişinden silmek istiyor musunuz?
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 429cbda0d..3f9865c47 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -27,7 +27,7 @@
顯示「用 Kodi 播放」的選項
預設音訊格式
WebM — 開放格式
- m4a — 更佳畫質
+ M4A — 更佳畫質
主題
暗色系
明亮色系
@@ -36,7 +36,7 @@
下一部影片
顯示下一部和相關的影片
不支援此網址
- 內容語言
+ 預設內容語言
影片和音訊
外觀
其他
@@ -62,7 +62,7 @@
勾選後,部分解析度可能不會有音訊
NewPipe 懸浮視窗模式
背景
- 從其他應用程式開啟時自動播放
+ 自動播放
當 NewPipe 被其他應用程式呼叫時自動播放影片
懸浮視窗預設解析度
顯示更高的解析度
@@ -97,14 +97,14 @@
重新設定大小
錯誤
無法載入所有縮圖
- 無法解析影片 URL 簽名。
- 無法解析網站。
- 無法完全解析網站。
- 內容無法使用。
- 已被 GEMA 阻擋。
- 無法設定下載選單。
+ 無法解析影片 URL 簽章
+ 無法解析網站
+ 無法完全解析網站
+ 內容無法使用
+ 已被 GEMA 阻擋
+ 無法設定下載選單
尚未支援現場直播。
- 無法取得串流。
+ 無法取得串流
無法載入圖片
應用程式或 UI 已停止運作
抱歉,這不應該發生的。
@@ -126,7 +126,7 @@
重試
無法存取儲存空間
使用舊的播放器
- 舊型內建媒體播放器。
+ 舊型內建媒體播放器
千
@@ -135,7 +135,7 @@
開始
暫停
- 檢視
+ 播放
刪除
檢查碼
@@ -151,8 +151,8 @@
NewPipe 下載中
輕觸顯示詳細資訊
請稍候…
- 已複製至剪貼簿。
- 請選擇下載資料夾。
+ 已複製至剪貼簿
+ 請選擇下載資料夾
使用懸浮視窗模式需要此權限
reCAPTCHA 驗證
@@ -162,4 +162,82 @@
懸浮視窗
現場直播
-
+ 訂閱
+ 已訂閱
+ 已取消訂閱頻道
+ 無法切換訂閱
+ 無法更新訂閱
+
+ 主選單
+ 訂閱項目
+
+ 有什麼新鮮事
+
+ 搜尋紀錄
+ 在本機儲存搜尋紀錄
+ 歷史紀錄
+ 記錄觀看過的影片
+ 在取得視窗焦點時繼續播放
+ 在干擾結束後繼續播放(例如有來電)
+ 播放器
+ 行為
+ 歷史紀錄
+ 播放清單
+ 復原
+
+ NewPipe 通知
+ NewPipe 背景播放與懸浮模式播放器的通知
+
+ 沒有結果
+ 空空如也
+
+ 無訂閱者
+
+ - %s 位訂閱者
+
+
+ 無觀看次數
+
+ - %s 次觀看
+
+
+ 沒有影片
+
+ - %s 部影片
+
+
+ 下載
+ 檔案名稱中允許的字元
+ 不符合設定的字元將會被替換為此字串
+ 替換為
+
+ 字母與數字
+ 大部分的特殊字元
+
+ 關於 NewPipe
+ 設定
+ 關於
+ 第三方授權
+ © %1$s 由 %2$s 使用 %3$s 授權條款發佈
+ 無法載入授權條款
+ 開啟網站
+ 關於
+ 貢獻者
+ 授權條款
+ 一款在 Android 上免費輕巧的 YouTube 前端。
+ 在 GitHub 上檢視
+ NewPipe 使用的授權條款
+ 不管你有什麼點子,翻譯、設計、程式碼整理,或者程式碼撰寫,我們永遠歡迎你來幫忙。完成的越多,NewPipe 也會更好!
+ 閱讀授權條款
+ 貢獻
+
+ 歷史紀錄
+ 已搜尋
+ 已觀看
+ 歷史紀錄已被停用
+ 歷史紀錄
+ 沒有歷史紀錄
+ 已清除歷史紀錄
+ 項目已刪除
+ 確定要刪除此項搜尋紀錄嗎?
+
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..1633e193a
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #CD201F
+
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index c779e8216..76cd10681 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -51,7 +51,7 @@
- MPEG-4
- WebM
- - 3GPP
+ - 3GP
- @string/video_mp4_key
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a5b3993d8..e798e62e9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -194,7 +194,7 @@
Start
Pause
- View
+ Play
Delete
Checksum
@@ -265,4 +265,5 @@
The history is empty
History cleared
Item deleted
+ Do you want to delete this item from search history?
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 6663177e6..fa37f0e5d 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -109,23 +109,47 @@
- @color/video_overlay_color
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/us/shandian/giga/get/get/DownloadManagerImplTest.java b/app/src/test/java/us/shandian/giga/get/DownloadManagerImplTest.java
similarity index 84%
rename from app/src/test/java/us/shandian/giga/get/get/DownloadManagerImplTest.java
rename to app/src/test/java/us/shandian/giga/get/DownloadManagerImplTest.java
index a7242ba10..6ff702273 100644
--- a/app/src/test/java/us/shandian/giga/get/get/DownloadManagerImplTest.java
+++ b/app/src/test/java/us/shandian/giga/get/DownloadManagerImplTest.java
@@ -1,4 +1,4 @@
-package us.shandian.giga.get.get;
+package us.shandian.giga.get;
import org.junit.Ignore;
import org.junit.Test;
@@ -153,4 +153,34 @@ public class DownloadManagerImplTest {
assertSame(missions.get(1), downloadManager.getMission(1));
}
+ @Test
+ public void sortByTimestamp() throws Exception {
+ ArrayList 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));
+ }
+
}
\ No newline at end of file
diff --git a/assets/BETA_new_pipe_icon_5.svg b/assets/BETA_new_pipe_icon_5.svg
new file mode 100644
index 000000000..9406f2469
--- /dev/null
+++ b/assets/BETA_new_pipe_icon_5.svg
@@ -0,0 +1,546 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+ BETA
+
+
diff --git a/assets/16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh.png b/assets/bitcoin_qr_code.png
similarity index 100%
rename from assets/16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh.png
rename to assets/bitcoin_qr_code.png
diff --git a/assets/bountysource_qr_code.png b/assets/bountysource_qr_code.png
new file mode 100644
index 000000000..4fe03236a
Binary files /dev/null and b/assets/bountysource_qr_code.png differ
diff --git a/build.gradle b/build.gradle
index 52a96908f..5c494fe59 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
@@ -16,7 +17,7 @@ allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }
- maven { url 'https://maven.google.com' }
+ google()
maven { url 'https://clojars.org/repo' }
}
}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64a8..ed88a042a 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f3ed0a0af..74bb77845 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Tue Mar 07 14:05:42 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
diff --git a/gradlew b/gradlew
index 91a7e269e..cccdd3d51 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
##############################################################################
##
@@ -6,47 +6,6 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
+cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index aec99730b..e95643d6a 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/screenshots/screenshot_1.png b/screenshots/screenshot_1.png
deleted file mode 100644
index f2fd046bc..000000000
Binary files a/screenshots/screenshot_1.png and /dev/null differ
diff --git a/screenshots/screenshot_2.png b/screenshots/screenshot_2.png
deleted file mode 100644
index 6330e420d..000000000
Binary files a/screenshots/screenshot_2.png and /dev/null differ
diff --git a/screenshots/screenshot_3.png b/screenshots/screenshot_3.png
deleted file mode 100644
index e6477b2cc..000000000
Binary files a/screenshots/screenshot_3.png and /dev/null differ
diff --git a/screenshots/screenshot_4.png b/screenshots/screenshot_4.png
deleted file mode 100644
index ea9d8b1c7..000000000
Binary files a/screenshots/screenshot_4.png and /dev/null differ
diff --git a/screenshots/screenshot_5.png b/screenshots/screenshot_5.png
deleted file mode 100644
index c8ddb9fa7..000000000
Binary files a/screenshots/screenshot_5.png and /dev/null differ
diff --git a/screenshots/screenshot_6.png b/screenshots/screenshot_6.png
deleted file mode 100644
index d4d5122eb..000000000
Binary files a/screenshots/screenshot_6.png and /dev/null differ
diff --git a/screenshots/screenshot_7.png b/screenshots/screenshot_7.png
deleted file mode 100644
index 9a1e71385..000000000
Binary files a/screenshots/screenshot_7.png and /dev/null differ
diff --git a/screenshots/screenshot_8.png b/screenshots/screenshot_8.png
deleted file mode 100644
index 3e73bb358..000000000
Binary files a/screenshots/screenshot_8.png and /dev/null differ
diff --git a/screenshots/screenshot_9.png b/screenshots/screenshot_9.png
deleted file mode 100644
index d76c53f73..000000000
Binary files a/screenshots/screenshot_9.png and /dev/null differ
diff --git a/screenshots/shot_1.png b/screenshots/shot_1.png
new file mode 100644
index 000000000..986f201a1
Binary files /dev/null and b/screenshots/shot_1.png differ
diff --git a/screenshots/shot_10.png b/screenshots/shot_10.png
new file mode 100644
index 000000000..7e840d6dd
Binary files /dev/null and b/screenshots/shot_10.png differ
diff --git a/screenshots/shot_2.png b/screenshots/shot_2.png
new file mode 100644
index 000000000..10c70b54d
Binary files /dev/null and b/screenshots/shot_2.png differ
diff --git a/screenshots/shot_3.png b/screenshots/shot_3.png
new file mode 100644
index 000000000..7e3af77c1
Binary files /dev/null and b/screenshots/shot_3.png differ
diff --git a/screenshots/shot_4.png b/screenshots/shot_4.png
new file mode 100644
index 000000000..cdaf96153
Binary files /dev/null and b/screenshots/shot_4.png differ
diff --git a/screenshots/shot_5.png b/screenshots/shot_5.png
new file mode 100644
index 000000000..64c0a164d
Binary files /dev/null and b/screenshots/shot_5.png differ
diff --git a/screenshots/shot_6.png b/screenshots/shot_6.png
new file mode 100644
index 000000000..8a7af3c16
Binary files /dev/null and b/screenshots/shot_6.png differ
diff --git a/screenshots/shot_7.png b/screenshots/shot_7.png
new file mode 100644
index 000000000..d6a9b1ce2
Binary files /dev/null and b/screenshots/shot_7.png differ
diff --git a/screenshots/shot_8.png b/screenshots/shot_8.png
new file mode 100644
index 000000000..5fe620d00
Binary files /dev/null and b/screenshots/shot_8.png differ
diff --git a/screenshots/shot_9.png b/screenshots/shot_9.png
new file mode 100644
index 000000000..b4d51b7e0
Binary files /dev/null and b/screenshots/shot_9.png differ