Merge branch 'dev' into master
							
								
								
									
										1
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -13,6 +13,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras | |||||||
| * Check whether your issue/feature is already fixed/implemented | * Check whether your issue/feature is already fixed/implemented | ||||||
| * If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome! | * If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome! | ||||||
| * We use English for development. Issues in other languages will be closed and ignored. | * We use English for development. Issues in other languages will be closed and ignored. | ||||||
|  | * Please only add *one* issue at a time. Do not put multiple issues into one thread. | ||||||
|  |  | ||||||
| ## Bug Fixing | ## Bug Fixing | ||||||
| * If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information. | * If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information. | ||||||
|   | |||||||
| @@ -10,9 +10,6 @@ android: | |||||||
|     # The SDK version used to compile NewPipe |     # The SDK version used to compile NewPipe | ||||||
|     - android-26 |     - android-26 | ||||||
|  |  | ||||||
|     # Additional components |  | ||||||
|     - extra-android-m2repository |  | ||||||
|  |  | ||||||
| script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest | script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest | ||||||
|  |  | ||||||
| licenses: | licenses: | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,34 +1,32 @@ | |||||||
|  | <p align="center"><a href="https://newpipe.schabi.org"><img src="assets/new_pipe_icon_5.png" width="150"/></a></p>  | ||||||
|  | <h2 align="center"><b>NewPipe</b></h2> | ||||||
|  | <h4 align="center">A free lightweight YouTube frontend for Android.</h4> | ||||||
|  | <p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png"/></a></p>  | ||||||
|  |  | ||||||
|  | <p align="center"> | ||||||
|  | <a href="https://github.com/TeamNewPipe/NewPipe" alt="GitHub release"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" /></a> | ||||||
|  | <a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPL v3"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg" /></a> | ||||||
|  | <a href="https://travis-ci.org/TeamNewPipe/NewPipe" alt="Build Status"><img src="https://travis-ci.org/TeamNewPipe/NewPipe.svg" /></a> | ||||||
|  | <a href="https://hosted.weblate.org/engage/NewPipe/" alt="Translation Status"><img src="https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg" /></a> | ||||||
|  | <a href="http://webchat.freenode.net/?channels=%23newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/IRC%20chat-%23newpipe-brightgreen.svg" /></a> | ||||||
|  | </p> | ||||||
|  | <hr /> | ||||||
|  | <p align="center"><a href="#screenshots">Screenshots</a> • <a href="#description">Description</a> • <a href="#features">Features</a> • <a href="#contribution">Contribution</a> • <a href="#donate">Donate</a> • <a href="#license">License</a></p> | ||||||
|  | <hr /> | ||||||
| WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS. | WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS. | ||||||
|  |  | ||||||
| # NewPipe |  | ||||||
| NewPipe: A free lightweight YouTube frontend for Android.   |  | ||||||
|  |  | ||||||
| [](https://newpipe.schabi.org) |  | ||||||
| [](https://f-droid.org/packages/org.schabi.newpipe/) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Project status: |  | ||||||
| [](https://hosted.weblate.org/engage/NewPipe/) |  | ||||||
| [](https://travis-ci.org/TeamNewPipe/NewPipe) |  | ||||||
|  |  | ||||||
| ## Donate |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| `16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh` |  | ||||||
|  |  | ||||||
| ## Screenshots | ## Screenshots | ||||||
|  |  | ||||||
| [<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png) | [<img src="screenshots/shot_1.png" width=160>](screenshots/shot_1.png) | ||||||
| [<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png) | [<img src="screenshots/shot_2.png" width=160>](screenshots/shot_2.png) | ||||||
| [<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png) | [<img src="screenshots/shot_3.png" width=160>](screenshots/shot_3.png) | ||||||
| [<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png) | [<img src="screenshots/shot_4.png" width=160>](screenshots/shot_4.png) | ||||||
| [<img src="screenshots/screenshot_5.png" width=160>](screenshots/screenshot_5.png) | [<img src="screenshots/shot_5.png" width=160>](screenshots/shot_5.png) | ||||||
| [<img src="screenshots/screenshot_6.png" width=160>](screenshots/screenshot_6.png) | [<img src="screenshots/shot_6.png" width=160>](screenshots/shot_6.png) | ||||||
| [<img src="screenshots/screenshot_7.png" width=160>](screenshots/screenshot_7.png) | [<img src="screenshots/shot_7.png" width=160>](screenshots/shot_7.png) | ||||||
| [<img src="screenshots/screenshot_8.png" width=160>](screenshots/screenshot_8.png) | [<img src="screenshots/shot_8.png" width=160>](screenshots/shot_8.png) | ||||||
| [<img src="screenshots/screenshot_9.png" width=160>](screenshots/screenshot_9.png) | [<img src="screenshots/shot_9.png" width=160>](screenshots/shot_9.png) | ||||||
|  | [<img src="screenshots/shot_10.png" width=160>](screenshots/shot_10.png) | ||||||
|  |  | ||||||
| ## Description | ## Description | ||||||
|  |  | ||||||
| @@ -39,7 +37,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only | |||||||
| * Search videos | * Search videos | ||||||
| * Display general information about a video | * Display general information about a video | ||||||
| * Watch YouTube videos | * Watch YouTube videos | ||||||
| * Listen to YouTube videos (experimental) | * Listen to YouTube videos | ||||||
| * Popup mode (floating player) | * Popup mode (floating player) | ||||||
| * Select the streaming player to watch the video with | * Select the streaming player to watch the video with | ||||||
| * Download videos  | * Download videos  | ||||||
| @@ -47,21 +45,23 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only | |||||||
| * Open a video in Kodi | * Open a video in Kodi | ||||||
| * Show Next/Related videos | * Show Next/Related videos | ||||||
| * Search YouTube in a specific language | * Search YouTube in a specific language | ||||||
| * Watch age restricted material | * Watch/Block age restricted material | ||||||
| * Display general information about channels | * Display general information about channels | ||||||
| * Search channels | * Search channels | ||||||
| * Watch videos from a channel | * Watch videos from a channel | ||||||
| * Orbot/Tor support (not yet directly) | * Orbot/Tor support (not yet directly) | ||||||
| * 1080p/2k/4k support | * 1080p/2k/4k support | ||||||
|  | * View history | ||||||
|  | * Subscribe to channels | ||||||
|  | * Search history | ||||||
|  | * Search/Watch Playlists | ||||||
|  |  | ||||||
| ### Coming Features | ### Coming Features | ||||||
|  |  | ||||||
|  | * Multiservice support (eg. SoundCloud) | ||||||
| * Bookmarks | * Bookmarks | ||||||
| * View history | * Watch as queues Playlists | ||||||
| * Search history | * Queuing videos | ||||||
| * Subscribe to channels |  | ||||||
| * Search/Watch Playlists |  | ||||||
| * Queeing videos |  | ||||||
| * Subtitles support | * Subtitles support | ||||||
| * livestream support | * livestream support | ||||||
| * ... and many more | * ... and many more | ||||||
| @@ -75,6 +75,22 @@ The more is done the better it gets! | |||||||
|  |  | ||||||
| If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md). | If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md). | ||||||
|  |  | ||||||
|  | ## Donate | ||||||
|  | If you like NewPipe we'd be happy about a donation. You can either donate via Bitcoin or BountySource. For further information about donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate/). | ||||||
|  |  | ||||||
|  | <table> | ||||||
|  | <tr> | ||||||
|  | <td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin" /></td> | ||||||
|  | <td><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR Code" width="100px"/></td> | ||||||
|  | <td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td> | ||||||
|  | </tr> | ||||||
|  | <tr> | ||||||
|  | <td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alz="Bountysource" width="190px" /></a></td> | ||||||
|  | <td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visit NewPipe at bountysource.com" width="100px"/></a></td> | ||||||
|  | <td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Check out how many bounties you can earn." /></a></td> | ||||||
|  | </tr> | ||||||
|  | </table> | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
| [](http://www.gnu.org/licenses/gpl-3.0.en.html)   | [](http://www.gnu.org/licenses/gpl-3.0.en.html)   | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,8 +8,8 @@ android { | |||||||
|         applicationId "org.schabi.newpipe" |         applicationId "org.schabi.newpipe" | ||||||
|         minSdkVersion 15 |         minSdkVersion 15 | ||||||
|         targetSdkVersion 26 |         targetSdkVersion 26 | ||||||
|         versionCode 38 |         versionCode 39 | ||||||
|         versionName "0.10.0" |         versionName "0.10.1" | ||||||
|  |  | ||||||
|         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||||||
|         vectorDrawables.useSupportLibrary = true |         vectorDrawables.useSupportLibrary = true | ||||||
| @@ -26,6 +26,9 @@ android { | |||||||
|             debuggable true |             debuggable true | ||||||
|             applicationIdSuffix ".debug" |             applicationIdSuffix ".debug" | ||||||
|         } |         } | ||||||
|  |         beta { | ||||||
|  |             applicationIdSuffix ".beta" | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lintOptions { |     lintOptions { | ||||||
| @@ -45,7 +48,7 @@ dependencies { | |||||||
|         exclude module: 'support-annotations' |         exclude module: 'support-annotations' | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     compile 'com.github.TeamNewPipe:NewPipeExtractor:7ae274b' |     compile 'com.github.TeamNewPipe:NewPipeExtractor:1df3f67' | ||||||
|  |  | ||||||
|     testCompile 'junit:junit:4.12' |     testCompile 'junit:junit:4.12' | ||||||
|     testCompile 'org.mockito:mockito-core:1.10.19' |     testCompile 'org.mockito:mockito-core:1.10.19' | ||||||
| @@ -62,7 +65,7 @@ dependencies { | |||||||
|     compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' |     compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' | ||||||
|     compile 'de.hdodenhof:circleimageview:2.1.0' |     compile 'de.hdodenhof:circleimageview:2.1.0' | ||||||
|     compile 'com.github.nirhart:parallaxscroll:1.0' |     compile 'com.github.nirhart:parallaxscroll:1.0' | ||||||
|     compile 'com.nononsenseapps:filepicker:3.0.0' |     compile 'com.nononsenseapps:filepicker:3.0.1' | ||||||
|     compile 'com.google.android.exoplayer:exoplayer:r2.5.1' |     compile 'com.google.android.exoplayer:exoplayer:r2.5.1' | ||||||
|  |  | ||||||
|     debugCompile 'com.facebook.stetho:stetho:1.5.0' |     debugCompile 'com.facebook.stetho:stetho:1.5.0' | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -25,3 +25,13 @@ | |||||||
| -dontwarn org.mozilla.javascript.tools.** | -dontwarn org.mozilla.javascript.tools.** | ||||||
| -dontwarn android.arch.util.paging.CountedDataSource | -dontwarn android.arch.util.paging.CountedDataSource | ||||||
| -dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource | -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.* <fields>; | ||||||
|  | } | ||||||
|  | -keepnames class * { @icepick.State *;} | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								app/src/beta/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,10 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools"> | ||||||
|  |  | ||||||
|  |     <application | ||||||
|  |         android:label="NewPipe Beta" | ||||||
|  |         tools:replace="android:label"> | ||||||
|  |     </application> | ||||||
|  |  | ||||||
|  | </manifest> | ||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/beta/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/beta/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/beta/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/beta/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 13 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/beta/res/mipmap-xxxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 19 KiB | 
| @@ -88,10 +88,14 @@ | |||||||
|         <service android:name="us.shandian.giga.service.DownloadManagerService"/> |         <service android:name="us.shandian.giga.service.DownloadManagerService"/> | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name="com.nononsenseapps.filepicker.FilePickerActivity" |             android:name=".util.FilePickerActivityHelper" | ||||||
|             android:label="@string/app_name" |             android:label="@string/app_name" | ||||||
|             android:launchMode="singleTop" |             android:theme="@style/FilePickerThemeDark"> | ||||||
|             android:theme="@style/FilePickerTheme"/> |             <intent-filter> | ||||||
|  |                 <action android:name="android.intent.action.GET_CONTENT" /> | ||||||
|  |                 <category android:name="android.intent.category.DEFAULT" /> | ||||||
|  |             </intent-filter> | ||||||
|  |         </activity> | ||||||
|  |  | ||||||
|         <activity |         <activity | ||||||
|             android:name=".ReCaptchaActivity" |             android:name=".ReCaptchaActivity" | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ import java.io.InterruptedIOException; | |||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| import javax.net.ssl.HttpsURLConnection; | import javax.net.ssl.HttpsURLConnection; | ||||||
| @@ -135,11 +134,8 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             in = new BufferedReader(new InputStreamReader(con.getInputStream())); |             in = new BufferedReader(new InputStreamReader(con.getInputStream())); | ||||||
|             for (Map.Entry<String, List<String>> entry : con.getHeaderFields().entrySet()) { |  | ||||||
|                 System.err.println(entry.getKey() + ": " + entry.getValue()); |  | ||||||
|             } |  | ||||||
|             String inputLine; |  | ||||||
|  |  | ||||||
|  |             String inputLine; | ||||||
|             while ((inputLine = in.readLine()) != null) { |             while ((inputLine = in.readLine()) != null) { | ||||||
|                 response.append(inputLine); |                 response.append(inputLine); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -43,9 +43,8 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected void handleUrl(String url) { |     protected void handleUrl(String url) { | ||||||
|         try { |         boolean success = NavigationHelper.openByLink(this, url); | ||||||
|             NavigationHelper.openByLink(this, url); |         if (!success) { | ||||||
|         } catch (Exception e) { |  | ||||||
|             Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show(); |             Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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.CREATION_DATE; | ||||||
| import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID; | 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.SERVICE_ID; | ||||||
| import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME; | import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME; | ||||||
|  |  | ||||||
| @@ -27,11 +28,20 @@ public interface SearchHistoryDAO extends HistoryDAO<SearchHistoryEntry> { | |||||||
|     @Override |     @Override | ||||||
|     int deleteAll(); |     int deleteAll(); | ||||||
|  |  | ||||||
|  |     @Query("DELETE FROM " + TABLE_NAME + " WHERE " + SEARCH + " = :query") | ||||||
|  |     int deleteAllWhereQuery(String query); | ||||||
|  |  | ||||||
|     @Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE) |     @Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE) | ||||||
|     @Override |     @Override | ||||||
|     Flowable<List<SearchHistoryEntry>> getAll(); |     Flowable<List<SearchHistoryEntry>> getAll(); | ||||||
|  |  | ||||||
|  |     @Query("SELECT * FROM " + TABLE_NAME + " GROUP BY " + SEARCH + ORDER_BY_CREATION_DATE + " LIMIT :limit") | ||||||
|  |     Flowable<List<SearchHistoryEntry>> getUniqueEntries(int limit); | ||||||
|  |  | ||||||
|     @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE) |     @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE) | ||||||
|     @Override |     @Override | ||||||
|     Flowable<List<SearchHistoryEntry>> listByService(int serviceId); |     Flowable<List<SearchHistoryEntry>> listByService(int serviceId); | ||||||
|  |  | ||||||
|  |     @Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SEARCH + " LIKE :query || '%' GROUP BY " + SEARCH + " LIMIT :limit") | ||||||
|  |     Flowable<List<SearchHistoryEntry>> getSimilarEntries(String query, int limit); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import android.arch.persistence.room.Index; | |||||||
| import android.arch.persistence.room.PrimaryKey; | import android.arch.persistence.room.PrimaryKey; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.channel.ChannelInfoItem; | 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_SERVICE_ID; | ||||||
| import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; | import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; | ||||||
| @@ -28,7 +29,7 @@ public class SubscriptionEntity { | |||||||
|     private long uid = 0; |     private long uid = 0; | ||||||
|  |  | ||||||
|     @ColumnInfo(name = SUBSCRIPTION_SERVICE_ID) |     @ColumnInfo(name = SUBSCRIPTION_SERVICE_ID) | ||||||
|     private int serviceId = -1; |     private int serviceId = Constants.NO_SERVICE_ID; | ||||||
|  |  | ||||||
|     @ColumnInfo(name = SUBSCRIPTION_URL) |     @ColumnInfo(name = SUBSCRIPTION_URL) | ||||||
|     private String url; |     private String url; | ||||||
|   | |||||||
| @@ -65,6 +65,7 @@ import org.schabi.newpipe.player.PopupVideoPlayer; | |||||||
| import org.schabi.newpipe.player.old.PlayVideoActivity; | import org.schabi.newpipe.player.old.PlayVideoActivity; | ||||||
| import org.schabi.newpipe.report.ErrorActivity; | import org.schabi.newpipe.report.ErrorActivity; | ||||||
| import org.schabi.newpipe.report.UserAction; | import org.schabi.newpipe.report.UserAction; | ||||||
|  | import org.schabi.newpipe.util.Constants; | ||||||
| import org.schabi.newpipe.util.ExtractorHelper; | import org.schabi.newpipe.util.ExtractorHelper; | ||||||
| import org.schabi.newpipe.util.InfoCache; | import org.schabi.newpipe.util.InfoCache; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
| @@ -110,7 +111,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement | |||||||
|     private boolean wasRelatedStreamsExpanded = false; |     private boolean wasRelatedStreamsExpanded = false; | ||||||
|  |  | ||||||
|     @State |     @State | ||||||
|     protected int serviceId = -1; |     protected int serviceId = Constants.NO_SERVICE_ID; | ||||||
|     @State |     @State | ||||||
|     protected String name; |     protected String name; | ||||||
|     @State |     @State | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import android.view.View; | |||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.ListExtractor; | import org.schabi.newpipe.extractor.ListExtractor; | ||||||
| import org.schabi.newpipe.extractor.ListInfo; | import org.schabi.newpipe.extractor.ListInfo; | ||||||
|  | import org.schabi.newpipe.util.Constants; | ||||||
|  |  | ||||||
| import java.util.Queue; | import java.util.Queue; | ||||||
|  |  | ||||||
| @@ -21,7 +22,7 @@ import io.reactivex.schedulers.Schedulers; | |||||||
| public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListFragment<I, ListExtractor.NextItemsResult> { | public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListFragment<I, ListExtractor.NextItemsResult> { | ||||||
|  |  | ||||||
|     @State |     @State | ||||||
|     protected int serviceId = -1; |     protected int serviceId = Constants.NO_SERVICE_ID; | ||||||
|     @State |     @State | ||||||
|     protected String name; |     protected String name; | ||||||
|     @State |     @State | ||||||
|   | |||||||
| @@ -2,14 +2,16 @@ package org.schabi.newpipe.fragments.list.search; | |||||||
|  |  | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  | import android.content.DialogInterface; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.SharedPreferences; | import android.content.SharedPreferences; | ||||||
| import android.os.Build; |  | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.preference.PreferenceManager; | import android.preference.PreferenceManager; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
| import android.support.annotation.Nullable; | import android.support.annotation.Nullable; | ||||||
| import android.support.v7.app.ActionBar; | 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.support.v7.widget.TooltipCompat; | ||||||
| import android.text.Editable; | import android.text.Editable; | ||||||
| import android.text.TextUtils; | import android.text.TextUtils; | ||||||
| @@ -25,35 +27,50 @@ import android.view.ViewGroup; | |||||||
| import android.view.animation.DecelerateInterpolator; | import android.view.animation.DecelerateInterpolator; | ||||||
| import android.view.inputmethod.EditorInfo; | import android.view.inputmethod.EditorInfo; | ||||||
| import android.view.inputmethod.InputMethodManager; | import android.view.inputmethod.InputMethodManager; | ||||||
| import android.widget.AdapterView; | import android.widget.EditText; | ||||||
| import android.widget.AutoCompleteTextView; |  | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.NewPipeDatabase; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
| import org.schabi.newpipe.ReCaptchaActivity; | 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.InfoItem; | ||||||
| import org.schabi.newpipe.extractor.ListExtractor; | import org.schabi.newpipe.extractor.ListExtractor; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
|  | import org.schabi.newpipe.extractor.StreamingService; | ||||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | import org.schabi.newpipe.extractor.search.SearchEngine; | ||||||
| import org.schabi.newpipe.extractor.search.SearchResult; | import org.schabi.newpipe.extractor.search.SearchResult; | ||||||
|  | import org.schabi.newpipe.fragments.BackPressable; | ||||||
| import org.schabi.newpipe.fragments.list.BaseListFragment; | import org.schabi.newpipe.fragments.list.BaseListFragment; | ||||||
| import org.schabi.newpipe.history.HistoryListener; | import org.schabi.newpipe.history.HistoryListener; | ||||||
| import org.schabi.newpipe.report.UserAction; | 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.ExtractorHelper; | ||||||
|  | import org.schabi.newpipe.util.LayoutManagerSmoothScroller; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | 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.ArrayList; | ||||||
|  | import java.util.Iterator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Queue; | import java.util.Queue; | ||||||
|  | import java.util.concurrent.Callable; | ||||||
| import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
| import icepick.State; | import icepick.State; | ||||||
|  | import io.reactivex.Flowable; | ||||||
| import io.reactivex.Notification; | import io.reactivex.Notification; | ||||||
| import io.reactivex.Observable; | import io.reactivex.Observable; | ||||||
|  | import io.reactivex.ObservableSource; | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | import io.reactivex.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.disposables.CompositeDisposable; | ||||||
| import io.reactivex.disposables.Disposable; | import io.reactivex.disposables.Disposable; | ||||||
|  | import io.reactivex.functions.BiFunction; | ||||||
| import io.reactivex.functions.Consumer; | import io.reactivex.functions.Consumer; | ||||||
| import io.reactivex.functions.Function; | import io.reactivex.functions.Function; | ||||||
| import io.reactivex.functions.Predicate; | import io.reactivex.functions.Predicate; | ||||||
| @@ -62,69 +79,78 @@ import io.reactivex.subjects.PublishSubject; | |||||||
|  |  | ||||||
| import static org.schabi.newpipe.util.AnimationUtils.animateView; | import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||||
|  |  | ||||||
| public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor.NextItemsResult> { | public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor.NextItemsResult> implements BackPressable { | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Search |     // 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. |      * 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 |     @State | ||||||
|     protected int filterItemCheckedId = -1; |     protected int filterItemCheckedId = -1; | ||||||
|     private SearchEngine.Filter filter = SearchEngine.Filter.ANY; |     private SearchEngine.Filter filter = SearchEngine.Filter.ANY; | ||||||
|  |  | ||||||
|     @State |     @State | ||||||
|     protected int serviceId = -1; |     protected int serviceId = Constants.NO_SERVICE_ID; | ||||||
|     @State |     @State | ||||||
|     protected String searchQuery = ""; |     protected String searchQuery; | ||||||
|  |     @State | ||||||
|  |     protected String lastSearchedQuery; | ||||||
|     @State |     @State | ||||||
|     protected boolean wasSearchFocused = false; |     protected boolean wasSearchFocused = false; | ||||||
|  |  | ||||||
|     private int currentPage = 0; |     private int currentPage = 0; | ||||||
|     private int currentNextPage = 0; |     private int currentNextPage = 0; | ||||||
|     private String searchLanguage; |     private String searchLanguage; | ||||||
|     private boolean showSuggestions = true; |     private boolean isSuggestionsEnabled = true; | ||||||
|  |  | ||||||
|     private PublishSubject<String> suggestionPublisher = PublishSubject.create(); |     private PublishSubject<String> suggestionPublisher = PublishSubject.create(); | ||||||
|     private Disposable searchDisposable; |     private Disposable searchDisposable; | ||||||
|     private Disposable suggestionWorkerDisposable; |     private Disposable suggestionDisposable; | ||||||
|  |     private CompositeDisposable disposables = new CompositeDisposable(); | ||||||
|  |  | ||||||
|     private SuggestionListAdapter suggestionListAdapter; |     private SuggestionListAdapter suggestionListAdapter; | ||||||
|  |     private SearchHistoryDAO searchHistoryDAO; | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Views |     // Views | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     private View searchToolbarContainer; |     private View searchToolbarContainer; | ||||||
|     private AutoCompleteTextView searchEditText; |     private EditText searchEditText; | ||||||
|     private View searchClear; |     private View searchClear; | ||||||
|  |  | ||||||
|  |     private View suggestionsPanel; | ||||||
|  |     private RecyclerView suggestionsRecyclerView; | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////*/ |     /*////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     public static SearchFragment getInstance(int serviceId, String query) { |     public static SearchFragment getInstance(int serviceId, String query) { | ||||||
|         SearchFragment searchFragment = new SearchFragment(); |         SearchFragment searchFragment = new SearchFragment(); | ||||||
|         searchFragment.setQuery(serviceId, query); |         searchFragment.setQuery(serviceId, query); | ||||||
|         searchFragment.searchOnResume(); |  | ||||||
|  |         if (!TextUtils.isEmpty(query)) { | ||||||
|  |             searchFragment.setSearchOnResume(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return searchFragment; |         return searchFragment; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Set wasLoading to true so when the fragment onResume is called, the initial search is done. |      * 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() { |     private void setSearchOnResume() { | ||||||
|         if (!TextUtils.isEmpty(searchQuery)) { |         wasLoading.set(true); | ||||||
|             wasLoading.set(true); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
| @@ -135,6 +161,16 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|     public void onAttach(Context context) { |     public void onAttach(Context context) { | ||||||
|         super.onAttach(context); |         super.onAttach(context); | ||||||
|         suggestionListAdapter = new SuggestionListAdapter(activity); |         suggestionListAdapter = new SuggestionListAdapter(activity); | ||||||
|  |         searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onCreate(Bundle savedInstanceState) { | ||||||
|  |         super.onCreate(savedInstanceState); | ||||||
|  |  | ||||||
|  |         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); | ||||||
|  |         isSuggestionsEnabled = preferences.getBoolean(getString(R.string.show_search_suggestions_key), true); | ||||||
|  |         searchLanguage = preferences.getString(getString(R.string.search_language_key), getString(R.string.default_language_value)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -142,14 +178,23 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         return inflater.inflate(R.layout.fragment_search, container, false); |         return inflater.inflate(R.layout.fragment_search, container, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onViewCreated(View rootView, Bundle savedInstanceState) { | ||||||
|  |         super.onViewCreated(rootView, savedInstanceState); | ||||||
|  |         showSearchOnStart(); | ||||||
|  |         initSearchListeners(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onPause() { |     public void onPause() { | ||||||
|         super.onPause(); |         super.onPause(); | ||||||
|  |  | ||||||
|         wasSearchFocused = searchEditText.hasFocus(); |         wasSearchFocused = searchEditText.hasFocus(); | ||||||
|  |  | ||||||
|         if (searchDisposable != null) searchDisposable.dispose(); |         if (searchDisposable != null) searchDisposable.dispose(); | ||||||
|         if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose(); |         if (suggestionDisposable != null) suggestionDisposable.dispose(); | ||||||
|         hideSoftKeyboard(searchEditText); |         if (disposables != null) disposables.clear(); | ||||||
|  |         hideKeyboardSearch(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -157,10 +202,6 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         if (DEBUG) Log.d(TAG, "onResume() called"); |         if (DEBUG) Log.d(TAG, "onResume() called"); | ||||||
|         super.onResume(); |         super.onResume(); | ||||||
|  |  | ||||||
|         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); |  | ||||||
|         showSuggestions = preferences.getBoolean(getString(R.string.show_search_suggestions_key), true); |  | ||||||
|         searchLanguage = preferences.getString(getString(R.string.search_language_key), getString(R.string.default_language_value)); |  | ||||||
|  |  | ||||||
|         if (!TextUtils.isEmpty(searchQuery)) { |         if (!TextUtils.isEmpty(searchQuery)) { | ||||||
|             if (wasLoading.getAndSet(false)) { |             if (wasLoading.getAndSet(false)) { | ||||||
|                 if (currentNextPage > currentPage) loadMoreItems(); |                 if (currentNextPage > currentPage) loadMoreItems(); | ||||||
| @@ -175,7 +216,16 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (suggestionWorkerDisposable == null || suggestionWorkerDisposable.isDisposed()) initSuggestionObserver(); |         if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver(); | ||||||
|  |  | ||||||
|  |         if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) { | ||||||
|  |             showKeyboardSearch(); | ||||||
|  |             showSuggestionsPanel(); | ||||||
|  |         } else { | ||||||
|  |             hideKeyboardSearch(); | ||||||
|  |             hideSuggestionsPanel(); | ||||||
|  |         } | ||||||
|  |         wasSearchFocused = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -188,17 +238,16 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|     @Override |     @Override | ||||||
|     public void onDestroy() { |     public void onDestroy() { | ||||||
|         super.onDestroy(); |         super.onDestroy(); | ||||||
|         if (!activity.isChangingConfigurations()) StateSaver.onDestroy(savedState); |  | ||||||
|  |  | ||||||
|         if (searchDisposable != null) searchDisposable.dispose(); |         if (searchDisposable != null) searchDisposable.dispose(); | ||||||
|         if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose(); |         if (suggestionDisposable != null) suggestionDisposable.dispose(); | ||||||
|  |         if (disposables != null) disposables.clear(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onActivityResult(int requestCode, int resultCode, Intent data) { |     public void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||||
|         switch (requestCode) { |         switch (requestCode) { | ||||||
|             case ReCaptchaActivity.RECAPTCHA_REQUEST: |             case ReCaptchaActivity.RECAPTCHA_REQUEST: | ||||||
|                 if (resultCode == Activity.RESULT_OK && searchQuery.length() != 0) { |                 if (resultCode == Activity.RESULT_OK && !TextUtils.isEmpty(searchQuery)) { | ||||||
|                     search(searchQuery); |                     search(searchQuery); | ||||||
|                 } else Log.e(TAG, "ReCaptcha failed"); |                 } else Log.e(TAG, "ReCaptcha failed"); | ||||||
|                 break; |                 break; | ||||||
| @@ -209,6 +258,23 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|  |     // Init | ||||||
|  |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void initViews(View rootView, Bundle savedInstanceState) { | ||||||
|  |         super.initViews(rootView, savedInstanceState); | ||||||
|  |         suggestionsPanel = rootView.findViewById(R.id.suggestions_panel); | ||||||
|  |         suggestionsRecyclerView = rootView.findViewById(R.id.suggestions_list); | ||||||
|  |         suggestionsRecyclerView.setAdapter(suggestionListAdapter); | ||||||
|  |         suggestionsRecyclerView.setLayoutManager(new LayoutManagerSmoothScroller(activity)); | ||||||
|  |  | ||||||
|  |         searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container); | ||||||
|  |         searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text); | ||||||
|  |         searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // State Saving |     // State Saving | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
| @@ -229,8 +295,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onSaveInstanceState(Bundle bundle) { |     public void onSaveInstanceState(Bundle bundle) { | ||||||
|         searchQuery = searchEditText != null && !TextUtils.isEmpty(searchEditText.getText().toString()) |         searchQuery = searchEditText != null ? searchEditText.getText().toString() : searchQuery; | ||||||
|                 ? searchEditText.getText().toString() : searchQuery; |  | ||||||
|         super.onSaveInstanceState(bundle); |         super.onSaveInstanceState(bundle); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -245,7 +310,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         } else { |         } else { | ||||||
|             if (searchEditText != null) { |             if (searchEditText != null) { | ||||||
|                 searchEditText.setText(""); |                 searchEditText.setText(""); | ||||||
|                 showSoftKeyboard(searchEditText); |                 showKeyboardSearch(); | ||||||
|             } |             } | ||||||
|             animateView(errorPanelRoot, false, 200); |             animateView(errorPanelRoot, false, 200); | ||||||
|         } |         } | ||||||
| @@ -266,12 +331,6 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         inflater.inflate(R.menu.menu_search, menu); |         inflater.inflate(R.menu.menu_search, menu); | ||||||
|  |  | ||||||
|         searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container); |  | ||||||
|         searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text); |  | ||||||
|         searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear); |  | ||||||
|         setupSearchView(); |  | ||||||
|  |  | ||||||
|         restoreFilterChecked(menu, filterItemCheckedId); |         restoreFilterChecked(menu, filterItemCheckedId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -301,14 +360,13 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|  |  | ||||||
|     private SearchEngine.Filter getFilterFromMenuId(int itemId) { |     private SearchEngine.Filter getFilterFromMenuId(int itemId) { | ||||||
|         switch (itemId) { |         switch (itemId) { | ||||||
|             case R.id.menu_filter_all: |  | ||||||
|                 return SearchEngine.Filter.ANY; |  | ||||||
|             case R.id.menu_filter_video: |             case R.id.menu_filter_video: | ||||||
|                 return SearchEngine.Filter.STREAM; |                 return SearchEngine.Filter.STREAM; | ||||||
|             case R.id.menu_filter_channel: |             case R.id.menu_filter_channel: | ||||||
|                 return SearchEngine.Filter.CHANNEL; |                 return SearchEngine.Filter.CHANNEL; | ||||||
|             case R.id.menu_filter_playlist: |             case R.id.menu_filter_playlist: | ||||||
|                 return SearchEngine.Filter.PLAYLIST; |                 return SearchEngine.Filter.PLAYLIST; | ||||||
|  |             case R.id.menu_filter_all: | ||||||
|             default: |             default: | ||||||
|                 return SearchEngine.Filter.ANY; |                 return SearchEngine.Filter.ANY; | ||||||
|         } |         } | ||||||
| @@ -320,9 +378,9 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|  |  | ||||||
|     private TextWatcher textWatcher; |     private TextWatcher textWatcher; | ||||||
|  |  | ||||||
|     private void setupSearchView() { |     private void showSearchOnStart() { | ||||||
|         searchEditText.setText(searchQuery != null ? searchQuery : ""); |         if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery); | ||||||
|         searchEditText.setAdapter(suggestionListAdapter); |         searchEditText.setText(searchQuery); | ||||||
|  |  | ||||||
|         if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) { |         if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) { | ||||||
|             searchToolbarContainer.setTranslationX(100); |             searchToolbarContainer.setTranslationX(100); | ||||||
| @@ -334,15 +392,10 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             searchToolbarContainer.setAlpha(1f); |             searchToolbarContainer.setAlpha(1f); | ||||||
|             searchToolbarContainer.setVisibility(View.VISIBLE); |             searchToolbarContainer.setVisibility(View.VISIBLE); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         initSearchListeners(); |  | ||||||
|  |  | ||||||
|         if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) showSoftKeyboard(searchEditText); |  | ||||||
|         else hideSoftKeyboard(searchEditText); |  | ||||||
|         wasSearchFocused = false; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void initSearchListeners() { |     private void initSearchListeners() { | ||||||
|  |         if (DEBUG) Log.d(TAG, "initSearchListeners() called"); | ||||||
|         searchClear.setOnClickListener(new View.OnClickListener() { |         searchClear.setOnClickListener(new View.OnClickListener() { | ||||||
|             @Override |             @Override | ||||||
|             public void onClick(View v) { |             public void onClick(View v) { | ||||||
| @@ -352,11 +405,9 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { |                 searchEditText.setText(""); | ||||||
|                     searchEditText.setText("", false); |                 suggestionListAdapter.setItems(new ArrayList<SuggestionItem>()); | ||||||
|                 } else searchEditText.setText(""); |                 showKeyboardSearch(); | ||||||
|                 suggestionListAdapter.updateAdapter(new ArrayList<String>()); |  | ||||||
|                 showSoftKeyboard(searchEditText); |  | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -366,7 +417,9 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             @Override |             @Override | ||||||
|             public void onClick(View v) { |             public void onClick(View v) { | ||||||
|                 if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); |                 if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); | ||||||
|                 searchEditText.showDropDown(); |                 if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) { | ||||||
|  |                     showSuggestionsPanel(); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -374,22 +427,24 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             @Override |             @Override | ||||||
|             public void onFocusChange(View v, boolean hasFocus) { |             public void onFocusChange(View v, boolean hasFocus) { | ||||||
|                 if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]"); |                 if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]"); | ||||||
|                 if (hasFocus) searchEditText.showDropDown(); |                 if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) { | ||||||
|  |                     showSuggestionsPanel(); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         searchEditText.setOnItemClickListener(new AdapterView.OnItemClickListener() { |         suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() { | ||||||
|             @Override |             @Override | ||||||
|             public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |             public void onSuggestionItemSelected(SuggestionItem item) { | ||||||
|                 if (DEBUG) { |                 search(item.query); | ||||||
|                     Log.d(TAG, "onItemClick() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]"); |                 searchEditText.setText(item.query); | ||||||
|                 } |             } | ||||||
|                 String s = suggestionListAdapter.getSuggestion(position); |  | ||||||
|                 if (DEBUG) Log.d(TAG, "onItemClick text = " + s); |             @Override | ||||||
|                 submitQuery(s); |             public void onSuggestionItemLongClick(SuggestionItem item) { | ||||||
|  |                 if (item.fromHistory) showDeleteSuggestionDialog(item); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         searchEditText.setThreshold(THRESHOLD_SUGGESTION); |  | ||||||
|  |  | ||||||
|         if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher); |         if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher); | ||||||
|         textWatcher = new TextWatcher() { |         textWatcher = new TextWatcher() { | ||||||
| @@ -404,32 +459,32 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             @Override |             @Override | ||||||
|             public void afterTextChanged(Editable s) { |             public void afterTextChanged(Editable s) { | ||||||
|                 String newText = searchEditText.getText().toString(); |                 String newText = searchEditText.getText().toString(); | ||||||
|                 if (!TextUtils.isEmpty(newText)) suggestionPublisher.onNext(newText); |                 suggestionPublisher.onNext(newText); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         searchEditText.addTextChangedListener(textWatcher); |         searchEditText.addTextChangedListener(textWatcher); | ||||||
|  |  | ||||||
|         searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { |         searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { | ||||||
|             @Override |             @Override | ||||||
|             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { |             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { | ||||||
|                 if (DEBUG) |                 if (DEBUG) { | ||||||
|                     Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]"); |                     Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]"); | ||||||
|  |                 } | ||||||
|                 if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) { |                 if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) { | ||||||
|                     submitQuery(searchEditText.getText().toString()); |                     search(searchEditText.getText().toString()); | ||||||
|                     return true; |                     return true; | ||||||
|                 } |                 } | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         if (suggestionWorkerDisposable == null || suggestionWorkerDisposable.isDisposed()) initSuggestionObserver(); |         if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void unsetSearchListeners() { |     private void unsetSearchListeners() { | ||||||
|  |         if (DEBUG) Log.d(TAG, "unsetSearchListeners() called"); | ||||||
|         searchClear.setOnClickListener(null); |         searchClear.setOnClickListener(null); | ||||||
|         searchClear.setOnLongClickListener(null); |         searchClear.setOnLongClickListener(null); | ||||||
|         searchEditText.setOnClickListener(null); |         searchEditText.setOnClickListener(null); | ||||||
|         searchEditText.setOnItemClickListener(null); |  | ||||||
|         searchEditText.setOnFocusChangeListener(null); |         searchEditText.setOnFocusChangeListener(null); | ||||||
|         searchEditText.setOnEditorActionListener(null); |         searchEditText.setOnEditorActionListener(null); | ||||||
|  |  | ||||||
| @@ -437,68 +492,166 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         textWatcher = null; |         textWatcher = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void showSoftKeyboard(View view) { |     private void showSuggestionsPanel() { | ||||||
|         if (DEBUG) Log.d(TAG, "showSoftKeyboard() called with: view = [" + view + "]"); |         if (DEBUG) Log.d(TAG, "showSuggestionsPanel() called"); | ||||||
|         if (view == null) return; |         animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         if (view.requestFocus()) { |     private void hideSuggestionsPanel() { | ||||||
|  |         if (DEBUG) Log.d(TAG, "hideSuggestionsPanel() called"); | ||||||
|  |         animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void showKeyboardSearch() { | ||||||
|  |         if (DEBUG) Log.d(TAG, "showKeyboardSearch() called"); | ||||||
|  |         if (searchEditText == null) return; | ||||||
|  |  | ||||||
|  |         if (searchEditText.requestFocus()) { | ||||||
|             InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); |             InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); | ||||||
|             imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); |             imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void hideSoftKeyboard(View view) { |     private void hideKeyboardSearch() { | ||||||
|         if (DEBUG) Log.d(TAG, "hideSoftKeyboard() called with: view = [" + view + "]"); |         if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called"); | ||||||
|         if (view == null) return; |         if (searchEditText == null) return; | ||||||
|  |  | ||||||
|         InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); |         InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); | ||||||
|         imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); |         imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); | ||||||
|  |  | ||||||
|         view.clearFocus(); |         searchEditText.clearFocus(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void showDeleteSuggestionDialog(final SuggestionItem item) { | ||||||
|  |         new AlertDialog.Builder(activity) | ||||||
|  |                 .setTitle(item.query) | ||||||
|  |                 .setMessage(R.string.delete_item_search_history) | ||||||
|  |                 .setCancelable(true) | ||||||
|  |                 .setNegativeButton(R.string.cancel, null) | ||||||
|  |                 .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onClick(DialogInterface dialog, int which) { | ||||||
|  |                         disposables.add(Observable | ||||||
|  |                                 .fromCallable(new Callable<Integer>() { | ||||||
|  |                                     @Override | ||||||
|  |                                     public Integer call() throws Exception { | ||||||
|  |                                         return searchHistoryDAO.deleteAllWhereQuery(item.query); | ||||||
|  |                                     } | ||||||
|  |                                 }) | ||||||
|  |                                 .subscribeOn(Schedulers.io()) | ||||||
|  |                                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                                 .subscribe(new Consumer<Integer>() { | ||||||
|  |                                     @Override | ||||||
|  |                                     public void accept(Integer howManyDeleted) throws Exception { | ||||||
|  |                                         suggestionPublisher.onNext(searchEditText.getText().toString()); | ||||||
|  |                                     } | ||||||
|  |                                 }, new Consumer<Throwable>() { | ||||||
|  |                                     @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() { |     public void giveSearchEditTextFocus() { | ||||||
|         showSoftKeyboard(searchEditText); |         showKeyboardSearch(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void initSuggestionObserver() { |     private void initSuggestionObserver() { | ||||||
|         if (suggestionWorkerDisposable != null) suggestionWorkerDisposable.dispose(); |         if (DEBUG) Log.d(TAG, "initSuggestionObserver() called"); | ||||||
|         final Predicate<String> checkEnabledAndLength = new Predicate<String>() { |         if (suggestionDisposable != null) suggestionDisposable.dispose(); | ||||||
|             @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<String>()); |  | ||||||
|                 } |  | ||||||
|                 // Only pass through if suggestions is enabled and the query length is equal or greater than THRESHOLD_SUGGESTION |  | ||||||
|                 return showSuggestions && lengthCheck; |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         suggestionWorkerDisposable = suggestionPublisher |         final Observable<String> observable = suggestionPublisher | ||||||
|                 .debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS) |                 .debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS) | ||||||
|                 .startWith(!TextUtils.isEmpty(searchQuery) ? searchQuery : "") |                 .startWith(searchQuery != null ? searchQuery : "") | ||||||
|                 .filter(checkEnabledAndLength) |                 .filter(new Predicate<String>() { | ||||||
|                 .switchMap(new Function<String, Observable<Notification<List<String>>>>() { |  | ||||||
|                     @Override |                     @Override | ||||||
|                     public Observable<Notification<List<String>>> apply(@io.reactivex.annotations.NonNull String query) throws Exception { |                     public boolean test(@io.reactivex.annotations.NonNull String query) throws Exception { | ||||||
|                         return ExtractorHelper.suggestionsFor(serviceId, query, searchLanguage).toObservable().materialize(); |                         return isSuggestionsEnabled; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |         suggestionDisposable = observable | ||||||
|  |                 .switchMap(new Function<String, ObservableSource<Notification<List<SuggestionItem>>>>() { | ||||||
|  |                     @Override | ||||||
|  |                     public ObservableSource<Notification<List<SuggestionItem>>> apply(@io.reactivex.annotations.NonNull final String query) throws Exception { | ||||||
|  |                         final Flowable<List<SearchHistoryEntry>> flowable = query.length() > 0 | ||||||
|  |                                 ? searchHistoryDAO.getSimilarEntries(query, 3) | ||||||
|  |                                 : searchHistoryDAO.getUniqueEntries(25); | ||||||
|  |                         final Observable<List<SuggestionItem>> local = flowable.toObservable() | ||||||
|  |                                 .map(new Function<List<SearchHistoryEntry>, List<SuggestionItem>>() { | ||||||
|  |                                     @Override | ||||||
|  |                                     public List<SuggestionItem> apply(@io.reactivex.annotations.NonNull List<SearchHistoryEntry> searchHistoryEntries) throws Exception { | ||||||
|  |                                         List<SuggestionItem> 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<List<SuggestionItem>> network = ExtractorHelper.suggestionsFor(serviceId, query, searchLanguage).toObservable() | ||||||
|  |                                 .map(new Function<List<String>, List<SuggestionItem>>() { | ||||||
|  |                                     @Override | ||||||
|  |                                     public List<SuggestionItem> apply(@io.reactivex.annotations.NonNull List<String> strings) throws Exception { | ||||||
|  |                                         List<SuggestionItem> result = new ArrayList<>(); | ||||||
|  |                                         for (String entry : strings) result.add(new SuggestionItem(false, entry)); | ||||||
|  |                                         return result; | ||||||
|  |                                     } | ||||||
|  |                                 }); | ||||||
|  |  | ||||||
|  |                         return Observable.zip(local, network, new BiFunction<List<SuggestionItem>, List<SuggestionItem>, List<SuggestionItem>>() { | ||||||
|  |                             @Override | ||||||
|  |                             public List<SuggestionItem> apply(@io.reactivex.annotations.NonNull List<SuggestionItem> localResult, @io.reactivex.annotations.NonNull List<SuggestionItem> networkResult) throws Exception { | ||||||
|  |                                 List<SuggestionItem> result = new ArrayList<>(); | ||||||
|  |                                 if (localResult.size() > 0) result.addAll(localResult); | ||||||
|  |  | ||||||
|  |                                 // Remove duplicates | ||||||
|  |                                 final Iterator<SuggestionItem> 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()) |                 .subscribeOn(Schedulers.io()) | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|                 .subscribe(new Consumer<Notification<List<String>>>() { |                 .subscribe(new Consumer<Notification<List<SuggestionItem>>>() { | ||||||
|                     @Override |                     @Override | ||||||
|                     public void accept(@io.reactivex.annotations.NonNull Notification<List<String>> listNotification) throws Exception { |                     public void accept(@io.reactivex.annotations.NonNull Notification<List<SuggestionItem>> listNotification) throws Exception { | ||||||
|                         if (listNotification.isOnNext()) { |                         if (listNotification.isOnNext()) { | ||||||
|                             handleSuggestions(listNotification.getValue()); |                             handleSuggestions(listNotification.getValue()); | ||||||
|                             if (errorPanelRoot.getVisibility() == View.VISIBLE) { |  | ||||||
|                                 hideLoading(); |  | ||||||
|                             } |  | ||||||
|                         } else if (listNotification.isOnError()) { |                         } else if (listNotification.isOnError()) { | ||||||
|                             Throwable error = listNotification.getError(); |                             Throwable error = listNotification.getError(); | ||||||
|                             if (!ExtractorHelper.isInterruptedCaused(error)) { |                             if (!ExtractorHelper.hasAssignableCauseThrowable(error, | ||||||
|  |                                     IOException.class, SocketException.class, InterruptedException.class, InterruptedIOException.class)) { | ||||||
|                                 onSuggestionError(error); |                                 onSuggestionError(error); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
| @@ -513,25 +666,58 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|  |  | ||||||
|     private void search(final String query) { |     private void search(final String query) { | ||||||
|         if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]"); |         if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]"); | ||||||
|  |         if (query.isEmpty()) return; | ||||||
|  |  | ||||||
|         hideSoftKeyboard(searchEditText); |         try { | ||||||
|         this.searchQuery = query; |             final StreamingService service = NewPipe.getServiceByUrl(query); | ||||||
|         this.currentPage = 0; |             if (service != null) { | ||||||
|  |                 showLoading(); | ||||||
|  |                 disposables.add(Observable | ||||||
|  |                         .fromCallable(new Callable<Intent>() { | ||||||
|  |                             @Override | ||||||
|  |                             public Intent call() throws Exception { | ||||||
|  |                                 return NavigationHelper.getIntentByLink(activity, service, query); | ||||||
|  |                             } | ||||||
|  |                         }) | ||||||
|  |                         .subscribeOn(Schedulers.io()) | ||||||
|  |                         .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                         .subscribe(new Consumer<Intent>() { | ||||||
|  |                             @Override | ||||||
|  |                             public void accept(Intent intent) throws Exception { | ||||||
|  |                                 getFragmentManager().popBackStackImmediate(); | ||||||
|  |                                 activity.startActivity(intent); | ||||||
|  |                             } | ||||||
|  |                         }, new Consumer<Throwable>() { | ||||||
|  |                             @Override | ||||||
|  |                             public void accept(Throwable throwable) throws Exception { | ||||||
|  |                                 showError(getString(R.string.url_not_supported_toast), false); | ||||||
|  |                             } | ||||||
|  |                         })); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             // Exception occurred, it's not a url | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lastSearchedQuery = query; | ||||||
|  |         searchQuery = query; | ||||||
|  |         currentPage = 0; | ||||||
|         infoListAdapter.clearStreamItemList(); |         infoListAdapter.clearStreamItemList(); | ||||||
|  |         hideSuggestionsPanel(); | ||||||
|  |         hideKeyboardSearch(); | ||||||
|  |  | ||||||
|         if (activity instanceof HistoryListener) { |         if (activity instanceof HistoryListener) { | ||||||
|             ((HistoryListener) activity).onSearch(serviceId, query); |             ((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); |         startLoading(false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void startLoading(boolean forceLoad) { |     public void startLoading(boolean forceLoad) { | ||||||
|         super.startLoading(forceLoad); |         super.startLoading(forceLoad); | ||||||
|  |         if (disposables != null) disposables.clear(); | ||||||
|         if (searchDisposable != null) searchDisposable.dispose(); |         if (searchDisposable != null) searchDisposable.dispose(); | ||||||
|         searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter) |         searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, searchLanguage, filter) | ||||||
|                 .subscribeOn(Schedulers.io()) |                 .subscribeOn(Schedulers.io()) | ||||||
| @@ -584,7 +770,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|     @Override |     @Override | ||||||
|     protected void onItemSelected(InfoItem selectedItem) { |     protected void onItemSelected(InfoItem selectedItem) { | ||||||
|         super.onItemSelected(selectedItem); |         super.onItemSelected(selectedItem); | ||||||
|         hideSoftKeyboard(searchEditText); |         hideKeyboardSearch(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
| @@ -595,13 +781,10 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         this.filter = filter; |         this.filter = filter; | ||||||
|         this.filterItemCheckedId = item.getItemId(); |         this.filterItemCheckedId = item.getItemId(); | ||||||
|         item.setChecked(true); |         item.setChecked(true); | ||||||
|         if (searchQuery != null && !searchQuery.isEmpty()) search(searchQuery); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void submitQuery(String query) { |         if (!TextUtils.isEmpty(searchQuery)) { | ||||||
|         if (DEBUG) Log.d(TAG, "submitQuery() called with: query = [" + query + "]"); |             search(searchQuery); | ||||||
|         if (query.isEmpty()) return; |         } | ||||||
|         search(query); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setQuery(int serviceId, String searchQuery) { |     private void setQuery(int serviceId, String searchQuery) { | ||||||
| @@ -609,19 +792,23 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         this.searchQuery = searchQuery; |         this.searchQuery = searchQuery; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void showError(String message, boolean showRetryButton) { |  | ||||||
|         super.showError(message, showRetryButton); |  | ||||||
|         hideSoftKeyboard(searchEditText); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Suggestion Results |     // Suggestion Results | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     public void handleSuggestions(@NonNull List<String> suggestions) { |     public void handleSuggestions(@NonNull final List<SuggestionItem> suggestions) { | ||||||
|         if (DEBUG) Log.d(TAG, "handleSuggestions() called with: suggestions = [" + 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) { |     public void onSuggestionError(Throwable exception) { | ||||||
| @@ -642,6 +829,13 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|         showListFooter(false); |         showListFooter(false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void showError(String message, boolean showRetryButton) { | ||||||
|  |         super.showError(message, showRetryButton); | ||||||
|  |         hideSuggestionsPanel(); | ||||||
|  |         hideKeyboardSearch(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Search Results |     // Search Results | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
| @@ -652,6 +846,8 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor | |||||||
|             showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0); |             showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         lastSearchedQuery = searchQuery; | ||||||
|  |  | ||||||
|         if (infoListAdapter.getItemsList().size() == 0) { |         if (infoListAdapter.getItemsList().size() == 0) { | ||||||
|             if (result.resultList.size() > 0) { |             if (result.resultList.size() > 0) { | ||||||
|                 infoListAdapter.addInfoItemList(result.resultList); |                 infoListAdapter.addInfoItemList(result.resultList); | ||||||
|   | |||||||
| @@ -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 + "]"; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,89 +1,108 @@ | |||||||
| package org.schabi.newpipe.fragments.list.search; | package org.schabi.newpipe.fragments.list.search; | ||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.database.Cursor; | import android.content.res.TypedArray; | ||||||
| import android.database.MatrixCursor; | import android.support.annotation.AttrRes; | ||||||
| import android.support.v4.widget.ResourceCursorAdapter; | import android.support.v7.widget.RecyclerView; | ||||||
|  | import android.view.LayoutInflater; | ||||||
| import android.view.View; | import android.view.View; | ||||||
|  | import android.view.ViewGroup; | ||||||
|  | import android.widget.ImageView; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.R; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| /* | public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAdapter.SuggestionItemHolder> { | ||||||
|  * Created by Christian Schabesberger on 02.08.16. |     private final ArrayList<SuggestionItem> items = new ArrayList<>(); | ||||||
|  * |     private final Context context; | ||||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> |     private OnSuggestionItemSelected listener; | ||||||
|  * 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 <http://www.gnu.org/licenses/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * {@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 interface OnSuggestionItemSelected { | ||||||
|  |         void onSuggestionItemSelected(SuggestionItem item); | ||||||
|  |         void onSuggestionItemLongClick(SuggestionItem item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public SuggestionListAdapter(Context context) { |     public SuggestionListAdapter(Context context) { | ||||||
|         super(context, android.R.layout.simple_list_item_1, null, 0); |         this.context = context; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setItems(List<SuggestionItem> items) { | ||||||
|  |         this.items.clear(); | ||||||
|  |         this.items.addAll(items); | ||||||
|  |         notifyDataSetChanged(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setListener(OnSuggestionItemSelected listener) { | ||||||
|  |         this.listener = listener; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void bindView(View view, Context context, Cursor cursor) { |     public SuggestionItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||||
|         ViewHolder viewHolder = new ViewHolder(view); |         return new SuggestionItemHolder(LayoutInflater.from(context).inflate(R.layout.item_search_suggestion, parent, false)); | ||||||
|         viewHolder.suggestionTitle.setText(cursor.getString(INDEX_TITLE)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     @Override | ||||||
|      * Update the suggestion list |     public void onBindViewHolder(SuggestionItemHolder holder, int position) { | ||||||
|      * @param suggestions the list of suggestions |         final SuggestionItem currentItem = getItem(position); | ||||||
|      */ |         holder.updateFrom(currentItem); | ||||||
|     public void updateAdapter(List<String> suggestions) { |         holder.itemView.setOnClickListener(new View.OnClickListener() { | ||||||
|         MatrixCursor cursor = new MatrixCursor(columns, suggestions.size()); |             @Override | ||||||
|         int i = 0; |             public void onClick(View v) { | ||||||
|         for (String suggestion : suggestions) { |                 if (listener != null) listener.onSuggestionItemSelected(currentItem); | ||||||
|             String[] columnValues = new String[columns.length]; |             } | ||||||
|             columnValues[INDEX_TITLE] = suggestion; |         }); | ||||||
|             columnValues[INDEX_ID] = Integer.toString(i); |         holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { | ||||||
|             cursor.addRow(columnValues); |             @Override | ||||||
|             i++; |             public boolean onLongClick(View v) { | ||||||
|  |                 if (listener != null) listener.onSuggestionItemLongClick(currentItem); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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); | ||||||
|         } |         } | ||||||
|         changeCursor(cursor); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |         private void updateFrom(SuggestionItem item) { | ||||||
|      * Get the suggestion for a position |             suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId); | ||||||
|      * @param position the position of the suggestion |             itemSuggestionQuery.setText(item.query); | ||||||
|      * @return the suggestion |         } | ||||||
|      */ |  | ||||||
|     public String getSuggestion(int position) { |  | ||||||
|         return ((Cursor) getItem(position)).getString(INDEX_TITLE); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |         private static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) { | ||||||
|     public CharSequence convertToString(Cursor cursor) { |             TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr}); | ||||||
|         return cursor.getString(INDEX_TITLE); |             int attributeResourceId = a.getResourceId(0, 0); | ||||||
|     } |             a.recycle(); | ||||||
|  |             return attributeResourceId; | ||||||
|     private class ViewHolder { |  | ||||||
|         private final TextView suggestionTitle; |  | ||||||
|         private ViewHolder(View view) { |  | ||||||
|             this.suggestionTitle = view.findViewById(android.R.id.text1); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -178,6 +178,10 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O | |||||||
|         if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]"); |         if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]"); | ||||||
|         initExoPlayerCache(); |         initExoPlayerCache(); | ||||||
|  |  | ||||||
|  |         if (audioManager == null) { | ||||||
|  |             this.audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); |         AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); | ||||||
|         DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory); |         DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory); | ||||||
|         DefaultLoadControl loadControl = new DefaultLoadControl(); |         DefaultLoadControl loadControl = new DefaultLoadControl(); | ||||||
|   | |||||||
| @@ -7,9 +7,8 @@ import android.support.annotation.Nullable; | |||||||
| import android.support.v7.preference.Preference; | import android.support.v7.preference.Preference; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
| import com.nononsenseapps.filepicker.FilePickerActivity; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.util.FilePickerActivityHelper; | ||||||
|  |  | ||||||
| public class DownloadSettingsFragment extends BasePreferenceFragment { | public class DownloadSettingsFragment extends BasePreferenceFragment { | ||||||
|     private static final int REQUEST_DOWNLOAD_PATH = 0x1235; |     private static final int REQUEST_DOWNLOAD_PATH = 0x1235; | ||||||
| @@ -48,10 +47,10 @@ public class DownloadSettingsFragment extends BasePreferenceFragment { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { |         if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { | ||||||
|             Intent i = new Intent(getActivity(), FilePickerActivity.class) |             Intent i = new Intent(getActivity(), FilePickerActivityHelper.class) | ||||||
|                     .putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false) |                     .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) | ||||||
|                     .putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true) |                     .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) | ||||||
|                     .putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR); |                     .putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR); | ||||||
|             if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) { |             if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) { | ||||||
|                 startActivityForResult(i, REQUEST_DOWNLOAD_PATH); |                 startActivityForResult(i, REQUEST_DOWNLOAD_PATH); | ||||||
|             } else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { |             } else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) { | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ public class AnimationUtils { | |||||||
|     private static final boolean DEBUG = MainActivity.DEBUG; |     private static final boolean DEBUG = MainActivity.DEBUG; | ||||||
|  |  | ||||||
|     public enum Type { |     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) { |     public static void animateView(View view, boolean enterOrExit, long duration) { | ||||||
| @@ -95,9 +95,16 @@ public class AnimationUtils { | |||||||
|             case LIGHT_SCALE_AND_ALPHA: |             case LIGHT_SCALE_AND_ALPHA: | ||||||
|                 animateLightScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd); |                 animateLightScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd); | ||||||
|                 break; |                 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 |      * Animate the background color of a view | ||||||
|      */ |      */ | ||||||
| @@ -237,4 +244,50 @@ public class AnimationUtils { | |||||||
|             }).start(); |             }).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(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,4 +9,6 @@ public class Constants { | |||||||
|     public static final String KEY_QUERY = "key_query"; |     public static final String KEY_QUERY = "key_query"; | ||||||
|  |  | ||||||
|     public static final String KEY_THEME_CHANGE = "key_theme_change"; |     public static final String KEY_THEME_CHANGE = "key_theme_change"; | ||||||
|  |  | ||||||
|  |     public static final int NO_SERVICE_ID = -1; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,7 +50,14 @@ public final class ExtractorHelper { | |||||||
|         //no instance |         //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<SearchResult> searchFor(final int serviceId, final String query, final int pageNumber, final String searchLanguage, final SearchEngine.Filter filter) { |     public static Single<SearchResult> searchFor(final int serviceId, final String query, final int pageNumber, final String searchLanguage, final SearchEngine.Filter filter) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return Single.fromCallable(new Callable<SearchResult>() { |         return Single.fromCallable(new Callable<SearchResult>() { | ||||||
|             @Override |             @Override | ||||||
|             public SearchResult call() throws Exception { |             public SearchResult call() throws Exception { | ||||||
| @@ -61,6 +68,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<NextItemsResult> getMoreSearchItems(final int serviceId, final String query, final int nextPageNumber, final String searchLanguage, final SearchEngine.Filter filter) { |     public static Single<NextItemsResult> 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) |         return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter) | ||||||
|                 .map(new Function<SearchResult, NextItemsResult>() { |                 .map(new Function<SearchResult, NextItemsResult>() { | ||||||
|                     @Override |                     @Override | ||||||
| @@ -71,6 +79,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<List<String>> suggestionsFor(final int serviceId, final String query, final String searchLanguage) { |     public static Single<List<String>> suggestionsFor(final int serviceId, final String query, final String searchLanguage) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return Single.fromCallable(new Callable<List<String>>() { |         return Single.fromCallable(new Callable<List<String>>() { | ||||||
|             @Override |             @Override | ||||||
|             public List<String> call() throws Exception { |             public List<String> call() throws Exception { | ||||||
| @@ -80,6 +89,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<StreamInfo> getStreamInfo(final int serviceId, final String url, boolean forceLoad) { |     public static Single<StreamInfo> getStreamInfo(final int serviceId, final String url, boolean forceLoad) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<StreamInfo>() { |         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<StreamInfo>() { | ||||||
|             @Override |             @Override | ||||||
|             public StreamInfo call() throws Exception { |             public StreamInfo call() throws Exception { | ||||||
| @@ -89,6 +99,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<ChannelInfo> getChannelInfo(final int serviceId, final String url, boolean forceLoad) { |     public static Single<ChannelInfo> getChannelInfo(final int serviceId, final String url, boolean forceLoad) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<ChannelInfo>() { |         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<ChannelInfo>() { | ||||||
|             @Override |             @Override | ||||||
|             public ChannelInfo call() throws Exception { |             public ChannelInfo call() throws Exception { | ||||||
| @@ -98,6 +109,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<NextItemsResult> getMoreChannelItems(final int serviceId, final String url, final String nextStreamsUrl) { |     public static Single<NextItemsResult> getMoreChannelItems(final int serviceId, final String url, final String nextStreamsUrl) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return Single.fromCallable(new Callable<NextItemsResult>() { |         return Single.fromCallable(new Callable<NextItemsResult>() { | ||||||
|             @Override |             @Override | ||||||
|             public NextItemsResult call() throws Exception { |             public NextItemsResult call() throws Exception { | ||||||
| @@ -107,6 +119,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId, final String url, boolean forceLoad) { |     public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId, final String url, boolean forceLoad) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<PlaylistInfo>() { |         return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<PlaylistInfo>() { | ||||||
|             @Override |             @Override | ||||||
|             public PlaylistInfo call() throws Exception { |             public PlaylistInfo call() throws Exception { | ||||||
| @@ -116,6 +129,7 @@ public final class ExtractorHelper { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Single<NextItemsResult> getMorePlaylistItems(final int serviceId, final String url, final String nextStreamsUrl) { |     public static Single<NextItemsResult> getMorePlaylistItems(final int serviceId, final String url, final String nextStreamsUrl) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return Single.fromCallable(new Callable<NextItemsResult>() { |         return Single.fromCallable(new Callable<NextItemsResult>() { | ||||||
|             @Override |             @Override | ||||||
|             public NextItemsResult call() throws Exception { |             public NextItemsResult call() throws Exception { | ||||||
| @@ -133,6 +147,7 @@ public final class ExtractorHelper { | |||||||
|      * and put the results in the cache. |      * and put the results in the cache. | ||||||
|      */ |      */ | ||||||
|     private static <I extends Info> Single<I> checkCache(boolean forceLoad, int serviceId, String url, Single<I> loadFromNetwork) { |     private static <I extends Info> Single<I> checkCache(boolean forceLoad, int serviceId, String url, Single<I> loadFromNetwork) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         loadFromNetwork = loadFromNetwork.doOnSuccess(new Consumer<I>() { |         loadFromNetwork = loadFromNetwork.doOnSuccess(new Consumer<I>() { | ||||||
|             @Override |             @Override | ||||||
|             public void accept(@NonNull I i) throws Exception { |             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 |      * Default implementation uses the {@link InfoCache} to get cached results | ||||||
|      */ |      */ | ||||||
|     public static <I extends Info> Maybe<I> loadFromCache(final int serviceId, final String url) { |     public static <I extends Info> Maybe<I> loadFromCache(final int serviceId, final String url) { | ||||||
|  |         checkServiceId(serviceId); | ||||||
|         return Maybe.defer(new Callable<MaybeSource<? extends I>>() { |         return Maybe.defer(new Callable<MaybeSource<? extends I>>() { | ||||||
|             @Override |             @Override | ||||||
|             public MaybeSource<? extends I> call() throws Exception { |             public MaybeSource<? extends I> call() throws Exception { | ||||||
|   | |||||||
| @@ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -14,7 +14,9 @@ import org.schabi.newpipe.R; | |||||||
| import org.schabi.newpipe.about.AboutActivity; | import org.schabi.newpipe.about.AboutActivity; | ||||||
| import org.schabi.newpipe.download.DownloadActivity; | import org.schabi.newpipe.download.DownloadActivity; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
|  | import org.schabi.newpipe.extractor.ServiceList; | ||||||
| import org.schabi.newpipe.extractor.StreamingService; | import org.schabi.newpipe.extractor.StreamingService; | ||||||
|  | import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||||
| import org.schabi.newpipe.extractor.stream.AudioStream; | import org.schabi.newpipe.extractor.stream.AudioStream; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.fragments.MainFragment; | import org.schabi.newpipe.fragments.MainFragment; | ||||||
| @@ -228,13 +230,17 @@ public class NavigationHelper { | |||||||
|     // Link handling |     // Link handling | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|     public static void openByLink(Context context, String url) throws Exception { |     public static boolean openByLink(Context context, String url) { | ||||||
|         Intent intentByLink = getIntentByLink(context, url); |         Intent intentByLink; | ||||||
|         if (intentByLink == null) |         try { | ||||||
|             throw new NullPointerException("getIntentByLink(context = [" + context + "], url = [" + url + "]) returned null"); |             intentByLink = getIntentByLink(context, url); | ||||||
|  |         } catch (ExtractionException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|         intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |         intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|         intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); |         intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); | ||||||
|         context.startActivity(intentByLink); |         context.startActivity(intentByLink); | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) { |     private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) { | ||||||
| @@ -245,14 +251,20 @@ public class NavigationHelper { | |||||||
|         return mIntent; |         return mIntent; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static Intent getIntentByLink(Context context, String url) throws Exception { |     public static Intent getIntentByLink(Context context, String url) throws ExtractionException { | ||||||
|         StreamingService service = NewPipe.getServiceByUrl(url); |         return getIntentByLink(context, NewPipe.getServiceByUrl(url), url); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Intent getIntentByLink(Context context, StreamingService service, String url) throws ExtractionException { | ||||||
|  |         if (service != ServiceList.YouTube.getService()) { | ||||||
|  |             throw new ExtractionException("Service not supported at the moment"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         int serviceId = service.getServiceId(); |         int serviceId = service.getServiceId(); | ||||||
|         StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); |         StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); | ||||||
|  |  | ||||||
|         if (linkType == StreamingService.LinkType.NONE) { |         if (linkType == StreamingService.LinkType.NONE) { | ||||||
|             throw new Exception("Url not known to service. service=" + serviceId + " url=" + url); |             throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         url = getCleanUrl(service, url, linkType); |         url = getCleanUrl(service, url, linkType); | ||||||
| @@ -268,7 +280,7 @@ public class NavigationHelper { | |||||||
|         return rIntent; |         return rIntent; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws Exception { |     private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException { | ||||||
|         switch (linkType) { |         switch (linkType) { | ||||||
|             case STREAM: |             case STREAM: | ||||||
|                 return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl); |                 return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl); | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ package org.schabi.newpipe.util; | |||||||
|  |  | ||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  | import android.os.Build; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.Parcel; | import android.os.Parcel; | ||||||
| import android.os.Parcelable; | import android.os.Parcelable; | ||||||
| @@ -29,6 +30,7 @@ import android.support.annotation.Nullable; | |||||||
| import android.text.TextUtils; | import android.text.TextUtils; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
|  |  | ||||||
|  | import org.schabi.newpipe.BuildConfig; | ||||||
| import org.schabi.newpipe.MainActivity; | import org.schabi.newpipe.MainActivity; | ||||||
|  |  | ||||||
| import java.io.File; | 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. |      * 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) { |     private static SavedState tryToRestore(@NonNull SavedState savedState, @NonNull WriteRead writeRead) { | ||||||
|         if (MainActivity.DEBUG) { |         if (MainActivity.DEBUG) { | ||||||
|             Log.d(TAG, "tryToRestore() called with: savedState = [" + savedState + "], writeRead = [" + writeRead + "]"); |             Log.d(TAG, "tryToRestore() called with: savedState = [" + savedState + "], writeRead = [" + writeRead + "]"); | ||||||
| @@ -117,7 +120,7 @@ public class StateSaver { | |||||||
|  |  | ||||||
|         FileInputStream fileInputStream = null; |         FileInputStream fileInputStream = null; | ||||||
|         try { |         try { | ||||||
|             Queue<Object> savedObjects = stateObjectsHolder.remove(savedState.prefixFileSaved); |             Queue<Object> savedObjects = stateObjectsHolder.remove(savedState.getPrefixFileSaved()); | ||||||
|             if (savedObjects != null) { |             if (savedObjects != null) { | ||||||
|                 writeRead.readFrom(savedObjects); |                 writeRead.readFrom(savedObjects); | ||||||
|                 if (MainActivity.DEBUG) { |                 if (MainActivity.DEBUG) { | ||||||
| @@ -126,8 +129,13 @@ public class StateSaver { | |||||||
|                 return savedState; |                 return savedState; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             File file = new File(savedState.pathFileSaved); |             File file = new File(savedState.getPathFileSaved()); | ||||||
|             if (!file.exists()) return null; |             if (!file.exists()) { | ||||||
|  |                 if(MainActivity.DEBUG) { | ||||||
|  |                     Log.d(TAG, "Cache file doesn't exist: " + file.getAbsolutePath()); | ||||||
|  |                 } | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             fileInputStream = new FileInputStream(file); |             fileInputStream = new FileInputStream(file); | ||||||
|             ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); |             ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); | ||||||
| @@ -139,7 +147,7 @@ public class StateSaver { | |||||||
|  |  | ||||||
|             return savedState; |             return savedState; | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             e.printStackTrace(); |             Log.e(TAG, "Failed to restore state", e); | ||||||
|         } finally { |         } finally { | ||||||
|             if (fileInputStream != null) { |             if (fileInputStream != null) { | ||||||
|                 try { |                 try { | ||||||
| @@ -154,10 +162,17 @@ public class StateSaver { | |||||||
|     /** |     /** | ||||||
|      * @see #tryToSave(boolean, String, String, WriteRead) |      * @see #tryToSave(boolean, String, String, WriteRead) | ||||||
|      */ |      */ | ||||||
|  |     @Nullable | ||||||
|     public static SavedState tryToSave(boolean isChangingConfig, @Nullable SavedState savedState, Bundle outState, WriteRead writeRead) { |     public static SavedState tryToSave(boolean isChangingConfig, @Nullable SavedState savedState, Bundle outState, WriteRead writeRead) { | ||||||
|         String currentSavedPrefix = savedState == null || TextUtils.isEmpty(savedState.prefixFileSaved) |         @NonNull | ||||||
|                 ? System.nanoTime() - writeRead.hashCode() + "" |         String currentSavedPrefix; | ||||||
|                 : savedState.prefixFileSaved; |         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); |         savedState = tryToSave(isChangingConfig, currentSavedPrefix, writeRead.generateSuffix(), writeRead); | ||||||
|         if (savedState != null) { |         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)}. |      * to the file with the name of prefixFileName + suffixFileName, in a cache folder got from the {@link #init(Context)}. | ||||||
|      * <p> |      * <p> | ||||||
|      * It checks if the file already exists and if it does, just return the path, so a good way to save is: |      * It checks if the file already exists and if it does, just return the path, so a good way to save is: | ||||||
|      * <li> A fixed prefix for the file |      * <ul> | ||||||
|      * <li> A changing suffix |      * <li> A fixed prefix for the file</li> | ||||||
|  |      * <li> A changing suffix</li> | ||||||
|  |      * </ul> | ||||||
|  |      * | ||||||
|  |      * @param isChangingConfig | ||||||
|  |      * @param prefixFileName | ||||||
|  |      * @param suffixFileName | ||||||
|  |      * @param writeRead | ||||||
|      */ |      */ | ||||||
|  |     @Nullable | ||||||
|     private static SavedState tryToSave(boolean isChangingConfig, final String prefixFileName, String suffixFileName, WriteRead writeRead) { |     private static SavedState tryToSave(boolean isChangingConfig, final String prefixFileName, String suffixFileName, WriteRead writeRead) { | ||||||
|         if (MainActivity.DEBUG) { |         if (MainActivity.DEBUG) { | ||||||
|             Log.d(TAG, "tryToSave() called with: isChangingConfig = [" + isChangingConfig + "], prefixFileName = [" + prefixFileName + "], suffixFileName = [" + suffixFileName + "], writeRead = [" + writeRead + "]"); |             Log.d(TAG, "tryToSave() called with: isChangingConfig = [" + isChangingConfig + "], prefixFileName = [" + prefixFileName + "], suffixFileName = [" + suffixFileName + "], writeRead = [" + writeRead + "]"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Queue<Object> savedObjects = new LinkedList<>(); |         LinkedList<Object> savedObjects = new LinkedList<>(); | ||||||
|         writeRead.writeTo(savedObjects); |         writeRead.writeTo(savedObjects); | ||||||
|  |  | ||||||
|         if (isChangingConfig) { |         if (isChangingConfig) { | ||||||
|             if (savedObjects.size() > 0) { |             if (savedObjects.size() > 0) { | ||||||
|                 stateObjectsHolder.put(prefixFileName, savedObjects); |                 stateObjectsHolder.put(prefixFileName, savedObjects); | ||||||
|                 return new SavedState(prefixFileName, ""); |                 return new SavedState(prefixFileName, ""); | ||||||
|             } else return null; |             } else { | ||||||
|  |                 if(MainActivity.DEBUG) Log.d(TAG, "Nothing to save"); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         FileOutputStream fileOutputStream = null; |         FileOutputStream fileOutputStream = null; | ||||||
| @@ -197,8 +223,12 @@ public class StateSaver { | |||||||
|             if (!cacheDir.exists()) throw new RuntimeException("Cache dir does not exist > " + cacheDirPath); |             if (!cacheDir.exists()) throw new RuntimeException("Cache dir does not exist > " + cacheDirPath); | ||||||
|             cacheDir = new File(cacheDir, CACHE_DIR_NAME); |             cacheDir = new File(cacheDir, CACHE_DIR_NAME); | ||||||
|             if (!cacheDir.exists()) { |             if (!cacheDir.exists()) { | ||||||
|                 boolean mkdirResult = cacheDir.mkdir(); |                 if(!cacheDir.mkdir()) { | ||||||
|                 if (!mkdirResult) return null; |                     if(BuildConfig.DEBUG) { | ||||||
|  |                         Log.e(TAG, "Failed to create cache directory " + cacheDir.getAbsolutePath()); | ||||||
|  |                     } | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (TextUtils.isEmpty(suffixFileName)) suffixFileName = ".cache"; |             if (TextUtils.isEmpty(suffixFileName)) suffixFileName = ".cache"; | ||||||
| @@ -214,7 +244,9 @@ public class StateSaver { | |||||||
|                         return name.contains(prefixFileName); |                         return name.contains(prefixFileName); | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|                 for (File file1 : files) file1.delete(); |                 for (File fileToDelete : files) { | ||||||
|  |                     fileToDelete.delete(); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             fileOutputStream = new FileOutputStream(file); |             fileOutputStream = new FileOutputStream(file); | ||||||
| @@ -223,7 +255,7 @@ public class StateSaver { | |||||||
|  |  | ||||||
|             return new SavedState(prefixFileName, file.getAbsolutePath()); |             return new SavedState(prefixFileName, file.getAbsolutePath()); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             e.printStackTrace(); |             Log.e(TAG, "Failed to save state", e); | ||||||
|         } finally { |         } finally { | ||||||
|             if (fileOutputStream != null) { |             if (fileOutputStream != null) { | ||||||
|                 try { |                 try { | ||||||
| @@ -241,11 +273,11 @@ public class StateSaver { | |||||||
|     public static void onDestroy(SavedState savedState) { |     public static void onDestroy(SavedState savedState) { | ||||||
|         if (MainActivity.DEBUG) Log.d(TAG, "onDestroy() called with: savedState = [" + savedState + "]"); |         if (MainActivity.DEBUG) Log.d(TAG, "onDestroy() called with: savedState = [" + savedState + "]"); | ||||||
|  |  | ||||||
|         if (savedState != null && !TextUtils.isEmpty(savedState.pathFileSaved)) { |         if (savedState != null && !TextUtils.isEmpty(savedState.getPathFileSaved())) { | ||||||
|             stateObjectsHolder.remove(savedState.prefixFileSaved); |             stateObjectsHolder.remove(savedState.getPrefixFileSaved()); | ||||||
|             try { |             try { | ||||||
|                 //noinspection ResultOfMethodCallIgnored |                 //noinspection ResultOfMethodCallIgnored | ||||||
|                 new File(savedState.pathFileSaved).delete(); |                 new File(savedState.getPathFileSaved()).delete(); | ||||||
|             } catch (Exception ignored) { |             } catch (Exception ignored) { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -271,9 +303,12 @@ public class StateSaver { | |||||||
|     // Inner |     // Inner | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Information about the saved state on the disk | ||||||
|  |      */ | ||||||
|     public static class SavedState implements Parcelable { |     public static class SavedState implements Parcelable { | ||||||
|         public String prefixFileSaved; |         private final String prefixFileSaved; | ||||||
|         public String pathFileSaved; |         private final String pathFileSaved; | ||||||
|  |  | ||||||
|         public SavedState(String prefixFileSaved, String pathFileSaved) { |         public SavedState(String prefixFileSaved, String pathFileSaved) { | ||||||
|             this.prefixFileSaved = prefixFileSaved; |             this.prefixFileSaved = prefixFileSaved; | ||||||
| @@ -287,7 +322,7 @@ public class StateSaver { | |||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public String toString() { |         public String toString() { | ||||||
|             return prefixFileSaved + " > " + pathFileSaved; |             return getPrefixFileSaved() + " > " + getPathFileSaved(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
| @@ -313,6 +348,22 @@ public class StateSaver { | |||||||
|                 return new SavedState[size]; |                 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; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -101,6 +101,18 @@ public class DownloadManagerImpl implements DownloadManager { | |||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Sort a list of mission by its timestamp. Oldest first | ||||||
|  |      * @param missions the missions to sort | ||||||
|  |      */ | ||||||
|  |     static void sortByTimestamp(List<DownloadMission> missions) { | ||||||
|  |         Collections.sort(missions, new Comparator<DownloadMission>() { | ||||||
|  |             @Override | ||||||
|  |             public int compare(DownloadMission o1, DownloadMission o2) { | ||||||
|  |                 return Long.valueOf(o1.timestamp).compareTo(o2.timestamp); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Loads finished missions from the data source |      * Loads finished missions from the data source | ||||||
| @@ -111,12 +123,8 @@ public class DownloadManagerImpl implements DownloadManager { | |||||||
|             finishedMissions = new ArrayList<>(); |             finishedMissions = new ArrayList<>(); | ||||||
|         } |         } | ||||||
|         // Ensure its sorted |         // Ensure its sorted | ||||||
|         Collections.sort(finishedMissions, new Comparator<DownloadMission>() { |         sortByTimestamp(finishedMissions); | ||||||
|             @Override |  | ||||||
|             public int compare(DownloadMission o1, DownloadMission o2) { |  | ||||||
|                 return (int) (o1.timestamp - o2.timestamp); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         mMissions.ensureCapacity(mMissions.size() + finishedMissions.size()); |         mMissions.ensureCapacity(mMissions.size() + finishedMissions.size()); | ||||||
|         for (DownloadMission mission : finishedMissions) { |         for (DownloadMission mission : finishedMissions) { | ||||||
|             File downloadedFile = mission.getDownloadedFile(); |             File downloadedFile = mission.getDownloadedFile(); | ||||||
|   | |||||||
| @@ -51,6 +51,25 @@ | |||||||
|  |  | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
|  |  | ||||||
|  |     <LinearLayout | ||||||
|  |         android:id="@+id/suggestions_panel" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:background="?android:attr/windowBackground" | ||||||
|  |         android:focusable="true" | ||||||
|  |         android:focusableInTouchMode="true" | ||||||
|  |         android:visibility="gone" | ||||||
|  |         tools:background="@android:color/transparent" | ||||||
|  |         tools:visibility="visible"> | ||||||
|  |  | ||||||
|  |         <android.support.v7.widget.RecyclerView | ||||||
|  |             android:id="@+id/suggestions_list" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent" | ||||||
|  |             android:scrollbars="vertical" | ||||||
|  |             tools:listitem="@layout/item_search_suggestion"/> | ||||||
|  |     </LinearLayout> | ||||||
|  |  | ||||||
|     <!--ERROR PANEL--> |     <!--ERROR PANEL--> | ||||||
|     <include |     <include | ||||||
|         android:id="@+id/error_panel" |         android:id="@+id/error_panel" | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								app/src/main/res/layout/item_search_suggestion.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,36 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <LinearLayout | ||||||
|  |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |     android:layout_width="match_parent" | ||||||
|  |     android:layout_height="wrap_content" | ||||||
|  |     android:background="?attr/selectableItemBackground" | ||||||
|  |     android:clickable="true" | ||||||
|  |     android:orientation="horizontal" | ||||||
|  |     android:paddingBottom="8dp" | ||||||
|  |     android:paddingTop="8dp"> | ||||||
|  |  | ||||||
|  |     <ImageView | ||||||
|  |         android:id="@+id/item_suggestion_icon" | ||||||
|  |         android:layout_width="24dp" | ||||||
|  |         android:layout_height="24dp" | ||||||
|  |         android:layout_gravity="center_vertical" | ||||||
|  |         android:layout_marginLeft="16dp" | ||||||
|  |         android:layout_marginRight="16dp" | ||||||
|  |         tools:ignore="ContentDescription,RtlHardcoded" | ||||||
|  |         tools:src="?attr/history"/> | ||||||
|  |  | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/item_suggestion_query" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:layout_gravity="center_vertical" | ||||||
|  |         android:layout_marginLeft="8dp" | ||||||
|  |         android:layout_marginRight="16dp" | ||||||
|  |         android:ellipsize="end" | ||||||
|  |         android:maxLines="2" | ||||||
|  |         android:textAppearance="@style/TextAppearance.AppCompat.Body1" | ||||||
|  |         android:textSize="14sp" | ||||||
|  |         tools:ignore="RtlHardcoded" | ||||||
|  |         tools:text="Search query"/> | ||||||
|  | </LinearLayout> | ||||||
| @@ -4,16 +4,9 @@ | |||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="?attr/actionBarSize" |     android:layout_height="?attr/actionBarSize" | ||||||
|     android:background="?attr/colorPrimary" |     android:background="?attr/colorPrimary"> | ||||||
|     android:focusable="true" |  | ||||||
|     android:focusableInTouchMode="true"> |  | ||||||
|  |  | ||||||
|     <View |     <EditText | ||||||
|         android:id="@+id/dropdown_anchor" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="match_parent"/> |  | ||||||
|  |  | ||||||
|     <AutoCompleteTextView |  | ||||||
|         android:id="@+id/toolbar_search_edit_text" |         android:id="@+id/toolbar_search_edit_text" | ||||||
|         android:layout_width="match_parent" |         android:layout_width="match_parent" | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
| @@ -24,7 +17,6 @@ | |||||||
|         android:background="?attr/colorPrimary" |         android:background="?attr/colorPrimary" | ||||||
|         android:drawableLeft="?attr/search" |         android:drawableLeft="?attr/search" | ||||||
|         android:drawablePadding="8dp" |         android:drawablePadding="8dp" | ||||||
|         android:dropDownAnchor="@+id/dropdown_anchor" |  | ||||||
|         android:focusable="true" |         android:focusable="true" | ||||||
|         android:focusableInTouchMode="true" |         android:focusableInTouchMode="true" | ||||||
|         android:hint="@string/search" |         android:hint="@string/search" | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> | ||||||
|  |     <background android:drawable="@color/ic_launcher_background"/> | ||||||
|  |     <foreground android:drawable="@mipmap/ic_launcher_foreground"/> | ||||||
|  | </adaptive-icon> | ||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 788 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.9 KiB | 
| @@ -2,7 +2,7 @@ | |||||||
| <resources> | <resources> | ||||||
|     <string name="main_bg_subtitle">Calca na gueta pa entamar</string> |     <string name="main_bg_subtitle">Calca na gueta pa entamar</string> | ||||||
|     <string name="view_count_text">%1$s visiones</string> |     <string name="view_count_text">%1$s visiones</string> | ||||||
|     <string name="upload_date_text">Espublizáu\'l %1$s</string> |     <string name="upload_date_text">Espublizóse\'l %1$s</string> | ||||||
|     <string name="no_player_found">Nun s\'alcontró un reproductor de fluxos. ¿Quies instalar VLC?</string> |     <string name="no_player_found">Nun s\'alcontró un reproductor de fluxos. ¿Quies instalar VLC?</string> | ||||||
|     <string name="install">Instalar</string> |     <string name="install">Instalar</string> | ||||||
|     <string name="cancel">Encaboxar</string> |     <string name="cancel">Encaboxar</string> | ||||||
| @@ -14,15 +14,15 @@ | |||||||
|     <string name="did_you_mean">¿Quixesti dicir %1$s?</string> |     <string name="did_you_mean">¿Quixesti dicir %1$s?</string> | ||||||
|     <string name="share_dialog_title">Compartir con</string> |     <string name="share_dialog_title">Compartir con</string> | ||||||
|     <string name="choose_browser">Escoyer restolador</string> |     <string name="choose_browser">Escoyer restolador</string> | ||||||
|     <string name="screen_rotation">rotación</string> |     <string name="screen_rotation">voltéu</string> | ||||||
|     <string name="use_external_video_player_title">Usar reproductor de videu esternu</string> |     <string name="use_external_video_player_title">Usar reproductor esternu de videu</string> | ||||||
|     <string name="use_external_audio_player_title">Usar reproductor d\'audiu esternu</string> |     <string name="use_external_audio_player_title">Usar reproductor esternu d\'audiu</string> | ||||||
|  |  | ||||||
|     <string name="download_path_title">Camín de descarga de vídeos</string> |     <string name="download_path_title">Camín de descarga de vídeos</string> | ||||||
|     <string name="download_path_summary">Camín nel qu\'atroxar los vídeos baxaos.</string> |     <string name="download_path_summary">Camín nel qu\'atroxar los vídeos baxaos</string> | ||||||
|     <string name="download_path_dialog_title">Introducir camín de descarga pa vídeos</string> |     <string name="download_path_dialog_title">Introducir camín de descarga pa vídeos</string> | ||||||
|  |  | ||||||
|     <string name="download_path_audio_title">Camín de descarga d\'audiu</string> |     <string name="download_path_audio_title">Camín de descarga p\'audios</string> | ||||||
|     <string name="download_path_audio_summary">Camín nel qu\'atroxar los audios baxaos.</string> |     <string name="download_path_audio_summary">Camín nel qu\'atroxar los audios baxaos.</string> | ||||||
|     <string name="download_path_audio_dialog_title">Introducir camín de descarga pa ficheros d\'audiu</string> |     <string name="download_path_audio_dialog_title">Introducir camín de descarga pa ficheros d\'audiu</string> | ||||||
|  |  | ||||||
| @@ -30,11 +30,11 @@ | |||||||
|     <string name="play_with_kodi_title">Reproducir con Kodi</string> |     <string name="play_with_kodi_title">Reproducir con Kodi</string> | ||||||
|     <string name="kore_not_found">Nun s\'alcontró Kore. ¿Instalalu?</string> |     <string name="kore_not_found">Nun s\'alcontró Kore. ¿Instalalu?</string> | ||||||
|     <string name="show_play_with_kodi_title">Amosar opción «Reproducir con Kodi»</string> |     <string name="show_play_with_kodi_title">Amosar opción «Reproducir con Kodi»</string> | ||||||
|     <string name="show_play_with_kodi_summary">Amuesa una opción pa reproducir un videu per Kodi.</string> |     <string name="show_play_with_kodi_summary">Amuesa una opción pa reproducir un videu per Kodi</string> | ||||||
|     <string name="play_audio">Audiu</string> |     <string name="play_audio">Audiu</string> | ||||||
|     <string name="default_audio_format_title">Formatu d\'audiu por defeutu</string> |     <string name="default_audio_format_title">Formatu por defeutu d\'audiu</string> | ||||||
|     <string name="webm_description">WebM — formatu llibre</string> |     <string name="webm_description">WebM — formatu llibre</string> | ||||||
|     <string name="m4a_description">m4a — calidá meyor</string> |     <string name="m4a_description">M4A — calidá meyor</string> | ||||||
|     <string name="theme_title">Tema</string> |     <string name="theme_title">Tema</string> | ||||||
|     <string name="dark_theme_title">Escuru</string> |     <string name="dark_theme_title">Escuru</string> | ||||||
|     <string name="light_theme_title">Claru</string> |     <string name="light_theme_title">Claru</string> | ||||||
| @@ -43,15 +43,15 @@ | |||||||
|     <string name="next_video_title">Videu siguiente</string> |     <string name="next_video_title">Videu siguiente</string> | ||||||
|     <string name="show_next_and_similar_title">Amosar vídeos siguientes y asemeyaos</string> |     <string name="show_next_and_similar_title">Amosar vídeos siguientes y asemeyaos</string> | ||||||
|     <string name="url_not_supported_toast">URL non sofitada</string> |     <string name="url_not_supported_toast">URL non sofitada</string> | ||||||
|     <string name="search_language_title">Llingua de conteníu preferíu</string> |     <string name="search_language_title">Llingua por defeutu del conteníu</string> | ||||||
|     <string name="settings_category_video_audio_title">Videu y audiu</string> |     <string name="settings_category_video_audio_title">Videu y audiu</string> | ||||||
|     <string name="settings_category_appearance_title">Aspeutu</string> |     <string name="settings_category_appearance_title">Aspeutu</string> | ||||||
|     <string name="settings_category_other_title">Otru</string> |     <string name="settings_category_other_title">Otru</string> | ||||||
|     <string name="background_player_playing_toast">Reproduciendo de fondu</string> |     <string name="background_player_playing_toast">Reproduciendo de fondu</string> | ||||||
|     <string name="play_btn_text">Reproducir</string> |     <string name="play_btn_text">Reproducir</string> | ||||||
|     <string name="content">Conteníu</string> |     <string name="content">Conteníu</string> | ||||||
|     <string name="show_age_restricted_content_title">Amosar conteníu restrinxíu pola edá</string> |     <string name="show_age_restricted_content_title">Amosar conteníu torgáu pola edá</string> | ||||||
|     <string name="video_is_age_restricted">El videu ta restrinxíu pola edá. Deshabilita esto diendo primero a axustes.</string> |     <string name="video_is_age_restricted">El videu ta torgáu pola edá. Desactiva esto diendo primero a axustes.</string> | ||||||
|     <string name="duration_live">en direuto</string> |     <string name="duration_live">en direuto</string> | ||||||
|     <string name="downloads">Descargues</string> |     <string name="downloads">Descargues</string> | ||||||
|     <string name="downloads_title">Descargues</string> |     <string name="downloads_title">Descargues</string> | ||||||
| @@ -60,16 +60,16 @@ | |||||||
|     <string name="general_error">Fallu</string> |     <string name="general_error">Fallu</string> | ||||||
|     <string name="network_error">Fallu de rede</string> |     <string name="network_error">Fallu de rede</string> | ||||||
|     <string name="could_not_load_thumbnails">Nun pudieron cargase toles miniatures</string> |     <string name="could_not_load_thumbnails">Nun pudieron cargase toles miniatures</string> | ||||||
|     <string name="youtube_signature_decryption_error">Nun pudo descargase la robla de la url del videu.</string> |     <string name="youtube_signature_decryption_error">Nun pudo descifrase la robla de la URL</string> | ||||||
|     <string name="parsing_error">Nun pudo analizase\'l sitiu web.</string> |     <string name="parsing_error">Nun pudo analizase\'l sitiu web</string> | ||||||
|     <string name="light_parsing_error">Nun pudo analizase dafechu\'l sitiu web.</string> |     <string name="light_parsing_error">Nun pudo analizase dafechu\'l sitiu web</string> | ||||||
|     <string name="content_not_available">Conteníu non disponible.</string> |     <string name="content_not_available">Conteníu non disponible</string> | ||||||
|     <string name="blocked_by_gema">Bloquiáu por GEMA.</string> |     <string name="blocked_by_gema">Bloquiáu por GEMA</string> | ||||||
|     <string name="could_not_setup_download_menu">Nun pudo configurase\'l menú de descarga.</string> |     <string name="could_not_setup_download_menu">Nun pudo configurase\'l menú de descarga</string> | ||||||
|     <string name="live_streams_not_supported">Esto ye una tresmisión de direuto pero entá nun ta sofitao.</string> |     <string name="live_streams_not_supported">Esto ye una tresmisión de direuto qu\'entá nun se sofita.</string> | ||||||
|     <string name="could_not_get_stream">Nun pudo consiguise tresmisión dala.</string> |     <string name="could_not_get_stream">Nun pudo consiguise tresmisión dala</string> | ||||||
|     <string name="sorry_string">Perdón, eso nun debió asoceder.</string> |     <string name="sorry_string">Perdón, eso nun debió asoceder.</string> | ||||||
|     <string name="error_report_button_text">Fallu d\'informe per corréu</string> |     <string name="error_report_button_text">Informar per corréu del fallu</string> | ||||||
|     <string name="error_snackbar_message">Perdón, asocedieron dellos fallos.</string> |     <string name="error_snackbar_message">Perdón, asocedieron dellos fallos.</string> | ||||||
|     <string name="error_snackbar_action">INFORMAR</string> |     <string name="error_snackbar_action">INFORMAR</string> | ||||||
|     <string name="what_device_headline">Información:</string> |     <string name="what_device_headline">Información:</string> | ||||||
| @@ -78,11 +78,11 @@ | |||||||
|     <string name="error_details_headline">Detalles:</string> |     <string name="error_details_headline">Detalles:</string> | ||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="list_thumbnail_view_description">Miniatura de previsualización de videu</string> |     <string name="list_thumbnail_view_description">Miniatura de previsualización del videu</string> | ||||||
|     <string name="detail_thumbnail_view_description">Miniatura de previsualización de videu</string> |     <string name="detail_thumbnail_view_description">Miniatura de previsualización del videu</string> | ||||||
|     <string name="detail_likes_img_view_description">Préstames</string> |     <string name="detail_likes_img_view_description">Préstames</string> | ||||||
|     <string name="use_tor_title">Usar Tor</string> |     <string name="use_tor_title">Usar Tor</string> | ||||||
|     <string name="use_tor_summary">(Esperimental) Forcia\'l tráficu de descargues pente Tor pa más privacidá (la tresmisión de vídeos entá nun ta sofitao).</string> |     <string name="use_tor_summary">(Esperimental) Forcia\'l tráficu de descargues pente Tor pa más privacidá (la tresmisión de vídeos entá nun se sofita).</string> | ||||||
|     <string name="report_error">Informa d\'un fallu</string> |     <string name="report_error">Informa d\'un fallu</string> | ||||||
|     <string name="user_report">Informe d\'usuariu</string> |     <string name="user_report">Informe d\'usuariu</string> | ||||||
|  |  | ||||||
| @@ -92,11 +92,11 @@ | |||||||
|     <string name="video">Videu</string> |     <string name="video">Videu</string> | ||||||
|     <string name="audio">Audiu</string> |     <string name="audio">Audiu</string> | ||||||
|     <string name="retry">Retentar</string> |     <string name="retry">Retentar</string> | ||||||
|     <string name="storage_permission_denied">Ñegóse l\'accesu al almacenamientu</string> |     <string name="storage_permission_denied">Ñegóse\'l permisu d\'accesu al almacenamientu</string> | ||||||
|  |  | ||||||
|     <string name="start">Aniciar</string> |     <string name="start">Aniciar</string> | ||||||
|     <string name="pause">Posar</string> |     <string name="pause">Posar</string> | ||||||
|     <string name="view">Ver</string> |     <string name="view">Reproducir</string> | ||||||
|     <string name="delete">Desaniciar</string> |     <string name="delete">Desaniciar</string> | ||||||
|     <string name="checksum">Suma de comprobación</string> |     <string name="checksum">Suma de comprobación</string> | ||||||
|  |  | ||||||
| @@ -110,23 +110,23 @@ | |||||||
|     <string name="msg_url_malform">URL malformada o internet non disponible</string> |     <string name="msg_url_malform">URL malformada o internet non disponible</string> | ||||||
|     <string name="msg_running_detail">Calca pa detallles</string> |     <string name="msg_running_detail">Calca pa detallles</string> | ||||||
|     <string name="msg_wait">Espera, por favor…</string> |     <string name="msg_wait">Espera, por favor…</string> | ||||||
|     <string name="msg_copied">Copióse al cartafueyu.</string> |     <string name="msg_copied">Copióse al cartafueyu</string> | ||||||
|     <string name="no_available_dir">Esbilla un direutoriu de descarga disponible, por favor.</string> |     <string name="no_available_dir">Esbilla una carpeta disponible de descarga, por favor</string> | ||||||
|  |  | ||||||
|     <string name="autoplay_by_calling_app_summary">Auto-reproduz un videu al llamar a NewPipe dende otra aplicación.</string> |     <string name="autoplay_by_calling_app_summary">Auto-reproduz un videu al llamar a NewPipe dende otra aplicación</string> | ||||||
|     <string name="autoplay_by_calling_app_title">Auto-reproducir al llamar dende otra aplicación</string> |     <string name="autoplay_by_calling_app_title">Auto-reproducción</string> | ||||||
|     <string name="detail_uploader_thumbnail_view_description">Miniatura del xubidor</string> |     <string name="detail_uploader_thumbnail_view_description">Miniatura del avatar del xubidor</string> | ||||||
|     <string name="detail_dislikes_img_view_description">Despréstames</string> |     <string name="detail_dislikes_img_view_description">Despréstames</string> | ||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="msg_running">NewPipe baxando</string> |     <string name="msg_running">NewPipe baxando</string> | ||||||
|     <string name="could_not_load_image">Nun pudo cargase la imaxe</string> |     <string name="could_not_load_image">Nun pudo cargase la imaxe</string> | ||||||
|     <string name="app_ui_crash">Cascó l\'aplicación/IU</string> |     <string name="app_ui_crash">Cascó l\'aplicación/IU</string> | ||||||
|     <string name="info_labels"/> |     <string name="info_labels">Lo qu\'asocedió:\\nSolicitú:\\nLlingua del conteníu:\\nServiciu:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del SO:\\nRangu global d\'IP:</string> | ||||||
|     <string name="open_in_popup_mode">Abrir en ventanu emerxente</string> |     <string name="open_in_popup_mode">Abrir en ventanu emerxente</string> | ||||||
|     <string name="popup_mode_share_menu_title">Mou de ventanu emerxente de NewPipe</string> |     <string name="popup_mode_share_menu_title">Mou de ventanu emerxente de NewPipe</string> | ||||||
|  |  | ||||||
|     <string name="default_video_format_title">Formatu preferíu de videu</string> |     <string name="default_video_format_title">Formatu por defeutu de videu</string> | ||||||
|     <string name="black_theme_title">Prietu</string> |     <string name="black_theme_title">Prietu</string> | ||||||
|  |  | ||||||
|     <string name="popup_playing_toast">Reproduciendo en ventanu emerxente</string> |     <string name="popup_playing_toast">Reproduciendo en ventanu emerxente</string> | ||||||
| @@ -134,7 +134,7 @@ | |||||||
|     <string name="channel">Canal</string> |     <string name="channel">Canal</string> | ||||||
|     <string name="yes">Sí</string> |     <string name="yes">Sí</string> | ||||||
|     <string name="later">Más sero</string> |     <string name="later">Más sero</string> | ||||||
|     <string name="disabled">Deshabilitóse</string> |     <string name="disabled">Desactivóse</string> | ||||||
|  |  | ||||||
|     <string name="use_old_player_title">Usar reproductor vieyu</string> |     <string name="use_old_player_title">Usar reproductor vieyu</string> | ||||||
|  |  | ||||||
| @@ -142,26 +142,26 @@ | |||||||
|     <string name="short_thousand">M</string> |     <string name="short_thousand">M</string> | ||||||
|     <string name="short_million">Mill</string> |     <string name="short_million">Mill</string> | ||||||
|     <string name="short_billion">MMill</string> |     <string name="short_billion">MMill</string> | ||||||
|     <string name="msg_popup_permission">Precísase esti permisu pa |     <string name="msg_popup_permission">Precísase esti permisu p\'abrir | ||||||
| abrir en ventanu emerxente</string> | \nen ventanu emerxente</string> | ||||||
|  |  | ||||||
|     <string name="reCaptchaActivity">reCAPTCHA</string> |     <string name="reCaptchaActivity">reCAPTCHA</string> | ||||||
|     <string name="reCaptcha_title">Prueba reCAPTCHA</string> |     <string name="reCaptcha_title">Prueba reCAPTCHA</string> | ||||||
|     <string name="recaptcha_request_toast">Prueba reCAPTCHA solicitada</string> |     <string name="recaptcha_request_toast">Solicitóse la prueba reCAPTCHA</string> | ||||||
|  |  | ||||||
|     <string name="controls_background_title">Fondu</string> |     <string name="controls_background_title">Fondu</string> | ||||||
|     <string name="controls_popup_title">Ventanu emerxente</string> |     <string name="controls_popup_title">Ventanu emerxente</string> | ||||||
|  |  | ||||||
|     <string name="default_popup_resolution_title">Resolución por defeutu de ventanu emerxente</string> |     <string name="default_popup_resolution_title">Resolución por defeutu del ventanu emerxente</string> | ||||||
|     <string name="show_higher_resolutions_title">Amosar resoluciones más altes</string> |     <string name="show_higher_resolutions_title">Amosar resoluciones más altes</string> | ||||||
|     <string name="show_higher_resolutions_summary">Namái dellos preseos sofiten vídeos en 2k/4k</string> |     <string name="show_higher_resolutions_summary">Namái dellos preseos sofiten vídeos en 2K/4K</string> | ||||||
|     <string name="filter">Peñera</string> |     <string name="filter">Peñera</string> | ||||||
|     <string name="refresh">Refrescar</string> |     <string name="refresh">Refrescar</string> | ||||||
|     <string name="clear">Llimpiar</string> |     <string name="clear">Llimpiar</string> | ||||||
|  |  | ||||||
|     <string name="use_external_video_player_summary">Delles resoluciones NUN tendrán audiu al habilitar esta opción</string> |     <string name="use_external_video_player_summary">Delles resoluciones NUN tendrán audiu al activar esta opción</string> | ||||||
|     <string name="popup_remember_size_pos_title">Tamañu y posición del ventanu emerxente</string> |     <string name="popup_remember_size_pos_title">Recuerdar tamañu y posición del ventanu emerxente</string> | ||||||
|     <string name="popup_remember_size_pos_summary">Recuerda la cabera posición y resolución afitada nel ventanu emerxente</string> |     <string name="popup_remember_size_pos_summary">Recuerda la cabera posición y resolución afitaes nel ventanu emerxente</string> | ||||||
|     <string name="player_gesture_controls_title">Controles per xestos del reproductor</string> |     <string name="player_gesture_controls_title">Controles per xestos del reproductor</string> | ||||||
|     <string name="player_gesture_controls_summary">Usa xestos pa controlar el brilléu y volume del reproductor</string> |     <string name="player_gesture_controls_summary">Usa xestos pa controlar el brilléu y volume del reproductor</string> | ||||||
|     <string name="show_search_suggestions_title">Suxerencies de gueta</string> |     <string name="show_search_suggestions_title">Suxerencies de gueta</string> | ||||||
| @@ -170,5 +170,87 @@ abrir en ventanu emerxente</string> | |||||||
|     <string name="settings_category_popup_title">Ventanu emerxente</string> |     <string name="settings_category_popup_title">Ventanu emerxente</string> | ||||||
|     <string name="popup_resizing_indicator_title">Redimensionáu</string> |     <string name="popup_resizing_indicator_title">Redimensionáu</string> | ||||||
|  |  | ||||||
|     <string name="use_old_player_summary">Compilación vieya del reproductor Mediaframework.</string> |     <string name="use_old_player_summary">Reproductor vieyu integráu de Mediaframework</string> | ||||||
|  | <string name="subscribe_button_title">Soscribise</string> | ||||||
|  |     <string name="subscribed_button_title">Soscribiéstite</string> | ||||||
|  |     <string name="channel_unsubscribed">Desoscribiéstite de la canal</string> | ||||||
|  |     <string name="subscription_change_failed">Nun pue camudase la resolución</string> | ||||||
|  |     <string name="subscription_update_failed">Nun pue anovase la soscripción</string> | ||||||
|  |  | ||||||
|  |     <string name="tab_main">Principal</string> | ||||||
|  |     <string name="tab_subscriptions">Soscripciones</string> | ||||||
|  |  | ||||||
|  |     <string name="fragment_whats_new">Qué hai nuevo</string> | ||||||
|  |  | ||||||
|  |     <string name="enable_search_history_title">Historial de gueta</string> | ||||||
|  |     <string name="enable_search_history_summary">Atroxa de mou llocal les solicitúes de gueta</string> | ||||||
|  |     <string name="enable_watch_history_title">Historial</string> | ||||||
|  |     <string name="enable_watch_history_summary">Fai un siguimientu de los vídeos vistos</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_title">Siguir al ganar el focu</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_summary">Sigue reproduciendo tres les interrupciones (por exemplu, llamaes de teléfonu)</string> | ||||||
|  |     <string name="settings_category_player_title">Reproductor</string> | ||||||
|  |     <string name="settings_category_player_behavior_title">Comportamientu</string> | ||||||
|  |     <string name="settings_category_history_title">Historial</string> | ||||||
|  |     <string name="playlist">Llistáu de reproducción</string> | ||||||
|  |     <string name="best_resolution">La meyor resolución</string> | ||||||
|  |     <string name="undo">Desfacer</string> | ||||||
|  |  | ||||||
|  |     <string name="notification_channel_name">Avisu de NewPipe</string> | ||||||
|  |     <string name="notification_channel_description">Avisos pa los reproductores de fondu y en ventanu emerxente de NewPipe</string> | ||||||
|  |  | ||||||
|  |     <string name="search_no_results">Ensin resultaos</string> | ||||||
|  |     <string name="empty_subscription_feed_subtitle">Equí nun hai más que grillos</string> | ||||||
|  |  | ||||||
|  |     <string name="no_subscribers">Ensin soscriptores</string> | ||||||
|  |     <plurals name="subscribers"> | ||||||
|  | 	<item quantity="one">%s soscriptor</item> | ||||||
|  | 	<item quantity="other">%s soscriptores</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_views">Ensin visionaos</string> | ||||||
|  |     <plurals name="views"> | ||||||
|  | 	<item quantity="one">%s visionáu</item> | ||||||
|  | 	<item quantity="other">%s visionaos</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_videos">Nun hai vídeos</string> | ||||||
|  |     <plurals name="videos"> | ||||||
|  | 	<item quantity="one">%s videu</item> | ||||||
|  | 	<item quantity="other">%s vídeos</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="settings_category_downloads_title">Descargues</string> | ||||||
|  |     <string name="settings_file_charset_title">Caráuteres permitíos nos nomes de ficheros</string> | ||||||
|  |     <string name="settings_file_replacement_character_summary">Los caráuteres non válidos tróquense por esti valor</string> | ||||||
|  |     <string name="settings_file_replacement_character_title">Troquéu de caráuteres</string> | ||||||
|  |  | ||||||
|  |     <string name="charset_letters_and_digits">Lletres y díxitos</string> | ||||||
|  |     <string name="charset_most_special_characters">La mayoría de caráuteres especiales</string> | ||||||
|  |  | ||||||
|  |     <string name="title_activity_about">Tocante a NewPipe</string> | ||||||
|  |     <string name="action_settings">Axustes</string> | ||||||
|  |     <string name="action_about">Tocante a</string> | ||||||
|  |     <string name="title_licenses">Llicencies de terceros</string> | ||||||
|  |     <string name="copyright" formatted="true">© %1$s por %2$s so la %3$s</string> | ||||||
|  |     <string name="error_unable_to_load_license">Nun pudo cargase la llicencia</string> | ||||||
|  |     <string name="action_open_website">Abrir sitiu web</string> | ||||||
|  |     <string name="tab_about">Tocante a</string> | ||||||
|  |     <string name="tab_contributors">Collaboradores</string> | ||||||
|  |     <string name="tab_licenses">Llicencies</string> | ||||||
|  |     <string name="app_description">Un frontal llixeru de YouTube p\'Android.</string> | ||||||
|  |     <string name="view_on_github">Ver en GitHub</string> | ||||||
|  |     <string name="app_license_title">Llicencia de NewPipe</string> | ||||||
|  |     <string name="contribution_encouragement">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!</string> | ||||||
|  |     <string name="read_full_license">Lleer llicencia</string> | ||||||
|  |     <string name="contribution_title">Collaboración</string> | ||||||
|  |  | ||||||
|  |     <string name="title_activity_history">Historial</string> | ||||||
|  |     <string name="title_history_search">Guetao</string> | ||||||
|  |     <string name="title_history_view">Visto</string> | ||||||
|  |     <string name="history_disabled">L\'historial ta desactiváu</string> | ||||||
|  |     <string name="action_history">Historial</string> | ||||||
|  |     <string name="history_empty">L\'historial ta baleru</string> | ||||||
|  |     <string name="history_cleared">Llimpióse l\'historial</string> | ||||||
|  |     <string name="item_deleted">Desanicióse l\'elementu</string> | ||||||
|  |     <string name="delete_item_search_history">¿Quies desaniciar esti elementu del historial de gueta?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -119,7 +119,7 @@ | |||||||
|  |  | ||||||
|     <string name="start">Starten</string> |     <string name="start">Starten</string> | ||||||
|     <string name="pause">Pause</string> |     <string name="pause">Pause</string> | ||||||
|     <string name="view">Ansehen</string> |     <string name="view">Abspielen</string> | ||||||
|     <string name="add">Neue Mission</string> |     <string name="add">Neue Mission</string> | ||||||
|     <string name="finish">OK</string> |     <string name="finish">OK</string> | ||||||
|     <string name="msg_server_unsupported">Server nicht unterstützt</string> |     <string name="msg_server_unsupported">Server nicht unterstützt</string> | ||||||
| @@ -254,8 +254,9 @@ | |||||||
|     <string name="charset_most_special_characters">Die meisten Sonderzeichen</string> |     <string name="charset_most_special_characters">Die meisten Sonderzeichen</string> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Element gelöscht</string> |     <string name="item_deleted">Element gelöscht</string> | ||||||
| <string name="resume_on_audio_focus_gain_title">Fortsetzen beim erneuten fokussieren</string> |     <string name="resume_on_audio_focus_gain_title">Fortsetzen bei erneutem Fokussieren</string> | ||||||
|     <string name="settings_category_player_title">Player</string> |     <string name="settings_category_player_title">Player</string> | ||||||
|     <string name="empty_subscription_feed_subtitle">Nichts hier außer Grillen</string> |     <string name="empty_subscription_feed_subtitle">Nichts hier außer Grillen</string> | ||||||
|  |  | ||||||
|     </resources> |     <string name="delete_item_search_history">Möchten Sie dieses Element aus dem Suchverlauf löschen?</string> | ||||||
|  | </resources> | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ | |||||||
|     <string name="show_age_restricted_content_title">Mostrar contenido restringido por edad</string> |     <string name="show_age_restricted_content_title">Mostrar contenido restringido por edad</string> | ||||||
|     <string name="video_is_age_restricted">Vídeo restringido por edad. Permitir este tipo de material es posible desde Ajustes.</string> |     <string name="video_is_age_restricted">Vídeo restringido por edad. Permitir este tipo de material es posible desde Ajustes.</string> | ||||||
|  |  | ||||||
|     <string name="main_bg_subtitle">Toque buscar para empezar</string> |     <string name="main_bg_subtitle">Toque en buscar para empezar</string> | ||||||
|     <string name="autoplay_by_calling_app_title">Autoreproducir</string> |     <string name="autoplay_by_calling_app_title">Autoreproducir</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">Reproducir automáticamente un vídeo cuando NewPipe es llamado desde otra aplicación</string> |     <string name="autoplay_by_calling_app_summary">Reproducir automáticamente un vídeo cuando NewPipe es llamado desde otra aplicación</string> | ||||||
|     <string name="duration_live">en vivo</string> |     <string name="duration_live">en vivo</string> | ||||||
| @@ -259,4 +259,5 @@ abrir en modo popup</string> | |||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Elemento eliminado</string> |     <string name="item_deleted">Elemento eliminado</string> | ||||||
|  | <string name="delete_item_search_history">¿Desea eliminar este elemento del historial de búsqueda?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|     <string name="settings">Paramètres</string> |     <string name="settings">Paramètres</string> | ||||||
|     <string name="share">Partager</string> |     <string name="share">Partager</string> | ||||||
|     <string name="share_dialog_title">Partager avec</string> |     <string name="share_dialog_title">Partager avec</string> | ||||||
|     <string name="show_play_with_kodi_summary">Afficher une option pour lire la vidéo via la médiathèque Kodi</string> |     <string name="show_play_with_kodi_summary">Afficher une option pour lire la vidéo via Kodi</string> | ||||||
|     <string name="show_play_with_kodi_title">Afficher l’option « Lire avec Kodi »</string> |     <string name="show_play_with_kodi_title">Afficher l’option « Lire avec Kodi »</string> | ||||||
|     <string name="upload_date_text">Ajoutée le %1$s</string> |     <string name="upload_date_text">Ajoutée le %1$s</string> | ||||||
|     <string name="view_count_text">%1$s vues</string> |     <string name="view_count_text">%1$s vues</string> | ||||||
| @@ -256,4 +256,5 @@ | |||||||
|     <string name="charset_most_special_characters">Caractères spéciaux</string> |     <string name="charset_most_special_characters">Caractères spéciaux</string> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Objet effacé</string> |     <string name="item_deleted">Objet effacé</string> | ||||||
|  | <string name="delete_item_search_history">Voulez-vous supprimer cet élément de l\'historique de recherche ?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ | |||||||
|     <string name="download_path_audio_summary">Lokasi untuk menyimpan audio yang diunduh</string> |     <string name="download_path_audio_summary">Lokasi untuk menyimpan audio yang diunduh</string> | ||||||
|     <string name="download_path_audio_dialog_title">Masukkan lokasi unduhan berkas audio</string> |     <string name="download_path_audio_dialog_title">Masukkan lokasi unduhan berkas audio</string> | ||||||
|  |  | ||||||
|     <string name="autoplay_by_calling_app_title">Putar otomatis ketika dipanggil dari aplikasi lain</string> |     <string name="autoplay_by_calling_app_title">Putar otomatis</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">Otomatis memutar video ketika NewPipe dijalankan dari aplikasi lain</string> |     <string name="autoplay_by_calling_app_summary">Otomatis memutar video ketika NewPipe dijalankan dari aplikasi lain</string> | ||||||
|     <string name="default_resolution_title">Resolusi baku</string> |     <string name="default_resolution_title">Resolusi baku</string> | ||||||
|     <string name="play_with_kodi_title">Putar dengan Kodi</string> |     <string name="play_with_kodi_title">Putar dengan Kodi</string> | ||||||
| @@ -33,7 +33,7 @@ | |||||||
|     <string name="play_audio">Audio</string> |     <string name="play_audio">Audio</string> | ||||||
|     <string name="default_audio_format_title">Format audio baku</string> |     <string name="default_audio_format_title">Format audio baku</string> | ||||||
|     <string name="webm_description">WebM — format bebas</string> |     <string name="webm_description">WebM — format bebas</string> | ||||||
|     <string name="m4a_description">m4a — kualitas lebih baik</string> |     <string name="m4a_description">M4A — kualitas lebih baik</string> | ||||||
|     <string name="theme_title">Tema</string> |     <string name="theme_title">Tema</string> | ||||||
|     <string name="dark_theme_title">Gelap</string> |     <string name="dark_theme_title">Gelap</string> | ||||||
|     <string name="light_theme_title">Terang</string> |     <string name="light_theme_title">Terang</string> | ||||||
| @@ -61,15 +61,15 @@ | |||||||
|     <string name="error_report_title">Laporan galat</string> |     <string name="error_report_title">Laporan galat</string> | ||||||
|  |  | ||||||
|     <string name="general_error">Galat</string> |     <string name="general_error">Galat</string> | ||||||
|     <string name="parsing_error">Tidak bisa mengurai situs web.</string> |     <string name="parsing_error">Tidak dapat mengurai situs web</string> | ||||||
|     <string name="light_parsing_error">Sama sekali tidak bisa mengurai situs web.</string> |     <string name="light_parsing_error">Sama sekali tidak dapat mengurai situs web</string> | ||||||
|     <string name="content_not_available">Konten tidak tersedia.</string> |     <string name="content_not_available">Konten tidak tersedia</string> | ||||||
|     <string name="blocked_by_gema">Diblokir oleh GEMA.</string> |     <string name="blocked_by_gema">Diblokir oleh GEMA</string> | ||||||
|     <string name="could_not_setup_download_menu">Tidak bisa menyiapkan menu unduhan.</string> |     <string name="could_not_setup_download_menu">Tidak bisa menyiapkan menu unduhan</string> | ||||||
|     <string name="live_streams_not_supported">Ini adalah SIARAN LANGSUNG. Fitur ini belum didukung.</string> |     <string name="live_streams_not_supported">Ini adalah SIARAN LANGSUNG, yang mana ini belum didukung.</string> | ||||||
|     <string name="could_not_load_image">Tidak bisa memuat gambar</string> |     <string name="could_not_load_image">Tidak bisa memuat gambar</string> | ||||||
|     <string name="sorry_string">Maaf, hal tersebut seharusnya tidak terjadi.</string> |     <string name="sorry_string">Maaf, hal tersebut seharusnya tidak terjadi.</string> | ||||||
|     <string name="error_report_button_text">Lapor galat via surel</string> |     <string name="error_report_button_text">Lapor galat via surat elektronik</string> | ||||||
|     <string name="error_snackbar_message">Maaf, telah terjadi galat.</string> |     <string name="error_snackbar_message">Maaf, telah terjadi galat.</string> | ||||||
|     <string name="error_snackbar_action">LAPOR</string> |     <string name="error_snackbar_action">LAPOR</string> | ||||||
|     <string name="what_device_headline">Info:</string> |     <string name="what_device_headline">Info:</string> | ||||||
| @@ -92,7 +92,7 @@ | |||||||
|     <string name="video">Video</string> |     <string name="video">Video</string> | ||||||
|     <string name="audio">Audio</string> |     <string name="audio">Audio</string> | ||||||
|     <string name="retry">Ulangi</string> |     <string name="retry">Ulangi</string> | ||||||
|     <string name="storage_permission_denied">Izin untuk mengakses penyimpanan ditolak</string> |     <string name="storage_permission_denied">Izin akses penyimpanan ditolak</string> | ||||||
|  |  | ||||||
|     <string name="delete">Hapus</string> |     <string name="delete">Hapus</string> | ||||||
|     <string name="view">Tonton</string> |     <string name="view">Tonton</string> | ||||||
| @@ -117,7 +117,7 @@ | |||||||
|     <string name="no_player_found">Tidak ditemukan pemutar stream. Apakah anda ingin memasang VLC?</string> |     <string name="no_player_found">Tidak ditemukan pemutar stream. Apakah anda ingin memasang VLC?</string> | ||||||
|     <string name="youtube_signature_decryption_error">Tidak bisa mendekrip tanda tangan URL video.</string> |     <string name="youtube_signature_decryption_error">Tidak bisa mendekrip tanda tangan URL video.</string> | ||||||
|     <string name="app_ui_crash">App/UI rusak</string> |     <string name="app_ui_crash">App/UI rusak</string> | ||||||
|     <string name="could_not_get_stream">Tidak bisa mendapatkan stream apapun.</string> |     <string name="could_not_get_stream">Tidak bisa mendapatkan stream apapun</string> | ||||||
|     <string name="info_labels">Apa:\\nPermintaan:\\nBahasa Konten:\\nLayanan:\\nWaktu GMT:\\nPaket:\\nVersi:\\nVersi OS:\\nIP:</string> |     <string name="info_labels">Apa:\\nPermintaan:\\nBahasa Konten:\\nLayanan:\\nWaktu GMT:\\nPaket:\\nVersi:\\nVersi OS:\\nIP:</string> | ||||||
|     <string name="user_report">Laporan pengguna</string> |     <string name="user_report">Laporan pengguna</string> | ||||||
|  |  | ||||||
| @@ -145,11 +145,11 @@ | |||||||
|     <string name="msg_popup_permission">Izin ini dibutuhkan untuk |     <string name="msg_popup_permission">Izin ini dibutuhkan untuk | ||||||
| membuka di mode popup</string> | membuka di mode popup</string> | ||||||
|  |  | ||||||
|     <string name="popup_mode_share_menu_title">Mode Popup NewPipe</string> |     <string name="popup_mode_share_menu_title">Mode popup NewPipe</string> | ||||||
|  |  | ||||||
|     <string name="popup_playing_toast">Memutar dalam mode popup</string> |     <string name="popup_playing_toast">Memutar dalam mode popup</string> | ||||||
|     <string name="use_old_player_title">Gunakan pemutar lama</string> |     <string name="use_old_player_title">Gunakan pemutar lama</string> | ||||||
|     <string name="use_old_player_summary">Versi lama pemutar Mediaframework.</string> |     <string name="use_old_player_summary">Versi lama pemutar Mediaframework</string> | ||||||
|     <string name="disabled">Dinonaktifkan</string> |     <string name="disabled">Dinonaktifkan</string> | ||||||
|  |  | ||||||
|     <string name="default_video_format_title">Pilihan format video</string> |     <string name="default_video_format_title">Pilihan format video</string> | ||||||
| @@ -166,7 +166,7 @@ membuka di mode popup</string> | |||||||
|     <string name="filter">Filter</string> |     <string name="filter">Filter</string> | ||||||
|     <string name="use_external_video_player_summary">Beberapa resolusi TIDAK akan memiliki suara ketika opsi ini diaktifkan</string> |     <string name="use_external_video_player_summary">Beberapa resolusi TIDAK akan memiliki suara ketika opsi ini diaktifkan</string> | ||||||
|     <string name="popup_remember_size_pos_title">Ingat ukuran dan posisi sembulan</string> |     <string name="popup_remember_size_pos_title">Ingat ukuran dan posisi sembulan</string> | ||||||
|     <string name="popup_remember_size_pos_summary">Ingat ukuran terakhir dan pengaturan posisi sembulan</string> |     <string name="popup_remember_size_pos_summary">Ingat ukuran terakhir dan pengaturan posisi popup</string> | ||||||
|  |  | ||||||
|     <string name="settings_category_popup_title">Sembulan</string> |     <string name="settings_category_popup_title">Sembulan</string> | ||||||
|     <string name="popup_resizing_indicator_title">Ubah ukuran</string> |     <string name="popup_resizing_indicator_title">Ubah ukuran</string> | ||||||
| @@ -212,7 +212,7 @@ membuka di mode popup</string> | |||||||
|     <string name="tab_main">Utama</string> |     <string name="tab_main">Utama</string> | ||||||
|     <string name="enable_search_history_title">Cari riwayat</string> |     <string name="enable_search_history_title">Cari riwayat</string> | ||||||
|     <string name="enable_search_history_summary">Simpan pencarian secara lokal</string> |     <string name="enable_search_history_summary">Simpan pencarian secara lokal</string> | ||||||
|     <string name="enable_watch_history_title">Riwayat tontonan</string> |     <string name="enable_watch_history_title">Riwayat</string> | ||||||
|     <string name="notification_channel_name">Notifikasi NewPipe</string> |     <string name="notification_channel_name">Notifikasi NewPipe</string> | ||||||
|     <string name="title_activity_history">Riwayat</string> |     <string name="title_activity_history">Riwayat</string> | ||||||
|     <string name="history_disabled">Riwayat dinonaktifkan</string> |     <string name="history_disabled">Riwayat dinonaktifkan</string> | ||||||
|   | |||||||
| @@ -12,16 +12,16 @@ | |||||||
|     <string name="settings">Impostazioni</string> |     <string name="settings">Impostazioni</string> | ||||||
|     <string name="did_you_mean">Intendevi: %1$s ?</string> |     <string name="did_you_mean">Intendevi: %1$s ?</string> | ||||||
|     <string name="share_dialog_title">Condividi con</string> |     <string name="share_dialog_title">Condividi con</string> | ||||||
|     <string name="choose_browser">Scegli browser</string> |     <string name="choose_browser">Scegli il browser</string> | ||||||
|     <string name="screen_rotation">rotazione</string> |     <string name="screen_rotation">rotazione</string> | ||||||
|     <string name="download_path_title">Cartella dei video scaricati</string> |     <string name="download_path_title">Percorso dei video scaricati</string> | ||||||
|     <string name="download_path_summary">Cartella in cui memorizzare i video scaricati.</string> |     <string name="download_path_summary">Percorso in cui memorizzare i video scaricati</string> | ||||||
|     <string name="download_path_dialog_title">Inserisci il percorso per i download</string> |     <string name="download_path_dialog_title">Inserisci il percorso per i download</string> | ||||||
|     <string name="default_resolution_title">Risoluzione predefinita</string> |     <string name="default_resolution_title">Risoluzione predefinita</string> | ||||||
|     <string name="play_with_kodi_title">Riproduci con Kodi</string> |     <string name="play_with_kodi_title">Riproduci con Kodi</string> | ||||||
|     <string name="kore_not_found">L\'applicazione Kore non è stata trovata. Vorresti installarla?</string> |     <string name="kore_not_found">L\'applicazione Kore non è stata trovata. Vuoi installarla?</string> | ||||||
|     <string name="show_play_with_kodi_title">Mostra l\'opzione \"Riproduci con Kodi\"</string> |     <string name="show_play_with_kodi_title">Mostra l\'opzione \"Riproduci con Kodi\"</string> | ||||||
|     <string name="show_play_with_kodi_summary">Mostra l\'opzione per riprodurre i video tramite Kodi.</string> |     <string name="show_play_with_kodi_summary">Mostra l\'opzione per riprodurre i video tramite Kodi</string> | ||||||
|     <string name="play_audio">Audio</string> |     <string name="play_audio">Audio</string> | ||||||
|     <string name="default_audio_format_title">Formato audio predefinito</string> |     <string name="default_audio_format_title">Formato audio predefinito</string> | ||||||
|     <string name="webm_description">WebM — formato libero</string> |     <string name="webm_description">WebM — formato libero</string> | ||||||
| @@ -30,7 +30,7 @@ | |||||||
|     <string name="next_video_title">Prossimo video</string> |     <string name="next_video_title">Prossimo video</string> | ||||||
|     <string name="show_next_and_similar_title">Mostra video a seguire e video simili</string> |     <string name="show_next_and_similar_title">Mostra video a seguire e video simili</string> | ||||||
|     <string name="url_not_supported_toast">URL non supportato</string> |     <string name="url_not_supported_toast">URL non supportato</string> | ||||||
|     <string name="search_language_title">Lingua preferita per i contenuti</string> |     <string name="search_language_title">Lingua predefinita per i contenuti</string> | ||||||
|     <string name="settings_category_video_audio_title">Video e Audio</string> |     <string name="settings_category_video_audio_title">Video e Audio</string> | ||||||
|  |  | ||||||
|     <string name="list_thumbnail_view_description">Anteprima video</string> |     <string name="list_thumbnail_view_description">Anteprima video</string> | ||||||
| @@ -40,12 +40,12 @@ | |||||||
|     <string name="detail_likes_img_view_description">Mi piace</string> |     <string name="detail_likes_img_view_description">Mi piace</string> | ||||||
|     <string name="err_dir_create">Impossibile creare la cartella di download \'%1$s\'</string> |     <string name="err_dir_create">Impossibile creare la cartella di download \'%1$s\'</string> | ||||||
|     <string name="info_dir_created">Creata la cartella per i download \'%1$s\'</string> |     <string name="info_dir_created">Creata la cartella per i download \'%1$s\'</string> | ||||||
|     <string name="use_external_video_player_title">Usa un lettore video esterno</string> |     <string name="use_external_video_player_title">Usa un riproduttore video esterno</string> | ||||||
|     <string name="use_external_audio_player_title">Usa un lettore audio esterno</string> |     <string name="use_external_audio_player_title">Usa un riproduttore audio esterno</string> | ||||||
|  |  | ||||||
|     <string name="download_path_audio_title">Cartella degli audio scaricati</string> |     <string name="download_path_audio_title">Percorso degli audio scaricati</string> | ||||||
|     <string name="download_path_audio_summary">Cartella dove salvare gli audio scaricati.</string> |     <string name="download_path_audio_summary">Percorso dove salvare gli audio scaricati</string> | ||||||
|     <string name="download_path_audio_dialog_title">Inserisci la cartella per i file audio</string> |     <string name="download_path_audio_dialog_title">Inserisci il percorso per i file audio</string> | ||||||
|  |  | ||||||
|     <string name="theme_title">Tema</string> |     <string name="theme_title">Tema</string> | ||||||
|     <string name="dark_theme_title">Scuro</string> |     <string name="dark_theme_title">Scuro</string> | ||||||
| @@ -63,26 +63,26 @@ | |||||||
|     <string name="content_not_available">Contenuto non disponibile</string> |     <string name="content_not_available">Contenuto non disponibile</string> | ||||||
|     <string name="blocked_by_gema">Bloccato dalla GEMA</string> |     <string name="blocked_by_gema">Bloccato dalla GEMA</string> | ||||||
|     <string name="use_tor_title">Usa Tor</string> |     <string name="use_tor_title">Usa Tor</string> | ||||||
|     <string name="use_tor_summary">(Sperimentale) Forza il traffico in download tramite Tor per una maggiore privacy (lo streaming dei video non è ancora supportato).</string> |     <string name="use_tor_summary">(Sperimentale) Forza il traffico in download tramite Tor per una maggiore riservatezza (lo streaming dei video non è ancora supportato).</string> | ||||||
|  |  | ||||||
|     <string name="parsing_error">Impossibile analizzare il sito web</string> |     <string name="parsing_error">Impossibile analizzare il sito web</string> | ||||||
|     <string name="could_not_setup_download_menu">Impossibile impostare il menù di download</string> |     <string name="could_not_setup_download_menu">Impossibile impostare il menù di download</string> | ||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="live_streams_not_supported">Questo è uno stream dal vivo. Gli stream dal vivo non sono ancora supportati.</string> |     <string name="live_streams_not_supported">Questo è uno stream in diretta, il quale non è ancora supportato.</string> | ||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="content">Contenuti</string> |     <string name="content">Contenuti</string> | ||||||
|     <string name="show_age_restricted_content_title">Mostra contenuti vincolati all\'età</string> |     <string name="show_age_restricted_content_title">Mostra contenuti vincolati all\'età</string> | ||||||
|     <string name="video_is_age_restricted">Questo video è riservato ad un pubblico maturo. Per accedervi, abilita \"Mostra video vincolati all\'età\" nelle impostazioni.</string> |     <string name="video_is_age_restricted">Questo video è riservato ad un pubblico maggiorenne. Per accedervi, abilita \"Mostra video vincolati all\'età\" nelle impostazioni.</string> | ||||||
|  |  | ||||||
|     <string name="main_bg_subtitle">Tocca \"cerca\" per iniziare</string> |     <string name="main_bg_subtitle">Tocca \"cerca\" per iniziare</string> | ||||||
|     <string name="autoplay_by_calling_app_title">Inizia automaticamente la riproduzione se NewPipe viene aperto da un\'altra app</string> |     <string name="autoplay_by_calling_app_title">Riproduzione automatica</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">Riproduci i video automaticamente quando NewPipe viene aperto da un\'altra app</string> |     <string name="autoplay_by_calling_app_summary">Riproduci i video automaticamente quando NewPipe viene aperto da un\'altra app</string> | ||||||
|     <string name="duration_live">in diretta</string> |     <string name="duration_live">in diretta</string> | ||||||
|  |  | ||||||
|     <string name="light_parsing_error">Impossibile eseguire il parsing completo del sito</string> |     <string name="light_parsing_error">Impossibile analizzare completamente il sito web</string> | ||||||
|     <string name="could_not_get_stream">Non è stato ottenuto alcuno stream</string> |     <string name="could_not_get_stream">Non è stato ottenuto alcun flusso</string> | ||||||
|     <string name="sorry_string">Ci dispiace, non sarebbe dovuto succedere.</string> |     <string name="sorry_string">Ci dispiace, non sarebbe dovuto succedere.</string> | ||||||
|     <string name="error_report_button_text">Segnala l\'errore via e-mail</string> |     <string name="error_report_button_text">Segnala l\'errore via e-mail</string> | ||||||
|     <string name="error_snackbar_message">Ci dispiace, c\'è stato qualche errore.</string> |     <string name="error_snackbar_message">Ci dispiace, c\'è stato qualche errore.</string> | ||||||
| @@ -99,18 +99,18 @@ | |||||||
|     <string name="video">Video</string> |     <string name="video">Video</string> | ||||||
|     <string name="audio">Audio</string> |     <string name="audio">Audio</string> | ||||||
|     <string name="retry">Riprova</string> |     <string name="retry">Riprova</string> | ||||||
|     <string name="storage_permission_denied">È stato negato il permesso di accedere all\'archiviazione di massa</string> |     <string name="storage_permission_denied">È stato negato il permesso di accesso all\'archiviazione di massa</string> | ||||||
|     <string name="downloads">Download</string> |     <string name="downloads">Download</string> | ||||||
|     <string name="downloads_title">Download</string> |     <string name="downloads_title">Download</string> | ||||||
|     <string name="error_report_title">Segnalazione errori</string> |     <string name="error_report_title">Segnalazione errori</string> | ||||||
|  |  | ||||||
|     <string name="start">Inizia</string> |     <string name="start">Inizia</string> | ||||||
|     <string name="pause">Pausa</string> |     <string name="pause">Pausa</string> | ||||||
|     <string name="view">Visualizza</string> |     <string name="view">Riproduci</string> | ||||||
|     <string name="delete">Elimina</string> |     <string name="delete">Elimina</string> | ||||||
|     <string name="checksum">Checksum</string> |     <string name="checksum">Codice di controllo</string> | ||||||
|  |  | ||||||
|     <string name="add">Nuova missione</string> |     <string name="add">Nuovo obiettivo</string> | ||||||
|     <string name="finish">OK</string> |     <string name="finish">OK</string> | ||||||
|  |  | ||||||
|     <string name="msg_name">Nome del file</string> |     <string name="msg_name">Nome del file</string> | ||||||
| @@ -126,7 +126,7 @@ | |||||||
|     <string name="no_available_dir">Seleziona una cartella disponibile in cui salvare i download</string> |     <string name="no_available_dir">Seleziona una cartella disponibile in cui salvare i download</string> | ||||||
|  |  | ||||||
|     <string name="could_not_load_image">Impossibile caricare l\'immagine</string> |     <string name="could_not_load_image">Impossibile caricare l\'immagine</string> | ||||||
|     <string name="app_ui_crash">L\'app/UI è andata in crash</string> |     <string name="app_ui_crash">L\'app/UI si è interrotta</string> | ||||||
|     <string name="info_labels">Cosa:\\nRichiesta:\\nLingua contenuto:\\nServizio:\\nOrario GMT:\\nPacchetto:\\nVersione:\\nVersione SO:\\nRange IP glob.:</string> |     <string name="info_labels">Cosa:\\nRichiesta:\\nLingua contenuto:\\nServizio:\\nOrario GMT:\\nPacchetto:\\nVersione:\\nVersione SO:\\nRange IP glob.:</string> | ||||||
|  |  | ||||||
|     <string name="reCaptchaActivity">reCAPTCHA</string> |     <string name="reCaptchaActivity">reCAPTCHA</string> | ||||||
| @@ -149,26 +149,26 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="open_in_popup_mode">Apri in modalità popup</string> |     <string name="open_in_popup_mode">Apri in modalità popup</string> | ||||||
|     <string name="popup_mode_share_menu_title">NewPipe Modo popup</string> |     <string name="popup_mode_share_menu_title">NewPipe in modalità popup</string> | ||||||
|  |  | ||||||
|     <string name="popup_playing_toast">Riproduzione in modalità popup</string> |     <string name="popup_playing_toast">Riproduzione in modalità popup</string> | ||||||
|     <string name="disabled">Disattivato</string> |     <string name="disabled">Disattivato</string> | ||||||
|  |  | ||||||
|     <string name="use_old_player_title">Usa il vecchio riproduttore</string> |     <string name="use_old_player_title">Usa il vecchio riproduttore</string> | ||||||
| <string name="use_external_video_player_summary">Alcune risoluzioni non avranno audio se questa opzione viene abilitata.</string> | <string name="use_external_video_player_summary">Alcune risoluzioni NON avranno l\'audio se questa opzione viene abilitata</string> | ||||||
|     <string name="controls_background_title">In sottofondo</string> |     <string name="controls_background_title">In sottofondo</string> | ||||||
|     <string name="controls_popup_title">Popup</string> |     <string name="controls_popup_title">Popup</string> | ||||||
|  |  | ||||||
|     <string name="default_popup_resolution_title">Risoluzione predefinita per il popup</string> |     <string name="default_popup_resolution_title">Risoluzione predefinita per la modalità popup</string> | ||||||
|     <string name="show_higher_resolutions_title">Mostra risoluzioni più alte</string> |     <string name="show_higher_resolutions_title">Mostra risoluzioni più alte</string> | ||||||
|     <string name="show_higher_resolutions_summary">Solo alcuni dispositivi supportano la riproduzione di video in 2K e 4K.</string> |     <string name="show_higher_resolutions_summary">Solo alcuni dispositivi supportano la riproduzione di video in 2K e 4K</string> | ||||||
|     <string name="default_video_format_title">Formato video preferito</string> |     <string name="default_video_format_title">Formato video predefinito</string> | ||||||
|     <string name="popup_remember_size_pos_title">Ricorda grandezza e posizione del popup</string> |     <string name="popup_remember_size_pos_title">Ricorda grandezza e posizione del popup</string> | ||||||
|     <string name="popup_remember_size_pos_summary">Ricorda l\'ultima grandezza e posizione del popup</string> |     <string name="popup_remember_size_pos_summary">Ricorda l\'ultima grandezza e posizione del popup</string> | ||||||
|     <string name="player_gesture_controls_title">Controlli gestuali</string> |     <string name="player_gesture_controls_title">Controlli gestuali</string> | ||||||
|     <string name="player_gesture_controls_summary">Usa i gesti per controllare luminosità e volume.</string> |     <string name="player_gesture_controls_summary">Usa i gesti per controllare luminosità e volume</string> | ||||||
|     <string name="show_search_suggestions_title">Suggerimenti di ricerca</string> |     <string name="show_search_suggestions_title">Suggerimenti di ricerca</string> | ||||||
|     <string name="show_search_suggestions_summary">Mostra suggerimenti durante la ricerca.</string> |     <string name="show_search_suggestions_summary">Mostra i suggerimenti durante la ricerca</string> | ||||||
|  |  | ||||||
|     <string name="settings_category_popup_title">Popup</string> |     <string name="settings_category_popup_title">Popup</string> | ||||||
|     <string name="filter">Filtra i risultati</string> |     <string name="filter">Filtra i risultati</string> | ||||||
| @@ -177,10 +177,10 @@ | |||||||
|     <string name="popup_resizing_indicator_title">Ridimensionamento</string> |     <string name="popup_resizing_indicator_title">Ridimensionamento</string> | ||||||
|     <string name="best_resolution">Risoluzione migliore</string> |     <string name="best_resolution">Risoluzione migliore</string> | ||||||
|  |  | ||||||
|     <string name="use_old_player_summary">Precedente riproduttore integrato facente uso di Mediaframework</string> |     <string name="use_old_player_summary">Precedente riproduttore integrato Mediaframework</string> | ||||||
|  |  | ||||||
|     <string name="msg_popup_permission">Questo permesso è necessario  |     <string name="msg_popup_permission">Questo permesso è necessario  | ||||||
| \nper la modalità popup</string> | \nper aprire la modalità popup</string> | ||||||
|  |  | ||||||
|     <string name="action_settings">Impostazioni</string> |     <string name="action_settings">Impostazioni</string> | ||||||
|     <string name="action_about">Informazioni</string> |     <string name="action_about">Informazioni</string> | ||||||
| @@ -206,7 +206,7 @@ | |||||||
|  |  | ||||||
|     <string name="tab_subscriptions">Iscrizioni</string> |     <string name="tab_subscriptions">Iscrizioni</string> | ||||||
|  |  | ||||||
|     <string name="fragment_whats_new">Nuovo</string> |     <string name="fragment_whats_new">Novità</string> | ||||||
|  |  | ||||||
|     <string name="enable_search_history_title">Cronologia ricerche</string> |     <string name="enable_search_history_title">Cronologia ricerche</string> | ||||||
|     <string name="enable_search_history_summary">Salva le ricerche localmente</string> |     <string name="enable_search_history_summary">Salva le ricerche localmente</string> | ||||||
| @@ -233,10 +233,10 @@ | |||||||
|     <string name="history_cleared">Cronologia cancellata</string> |     <string name="history_cleared">Cronologia cancellata</string> | ||||||
|  |  | ||||||
| <string name="tab_main">Principale</string> | <string name="tab_main">Principale</string> | ||||||
|     <string name="settings_category_player_title">Player</string> |     <string name="settings_category_player_title">Riproduttore</string> | ||||||
|     <string name="settings_category_player_behavior_title">Comportamento</string> |     <string name="settings_category_player_behavior_title">Comportamento</string> | ||||||
|     <string name="settings_category_history_title">Cronologia</string> |     <string name="settings_category_history_title">Cronologia</string> | ||||||
|     <string name="playlist">Playlist</string> |     <string name="playlist">Scaletta</string> | ||||||
|     <string name="undo">Annulla</string> |     <string name="undo">Annulla</string> | ||||||
|  |  | ||||||
|     <string name="notification_channel_name">Notifiche NewPipe</string> |     <string name="notification_channel_name">Notifiche NewPipe</string> | ||||||
| @@ -262,4 +262,7 @@ | |||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Elemento eliminato</string> |     <string name="item_deleted">Elemento eliminato</string> | ||||||
|  | <string name="empty_subscription_feed_subtitle">Nulla da mostrare</string> | ||||||
|  |  | ||||||
|  |     <string name="delete_item_search_history">Vuoi eliminare questo elemento dalla cronologia?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -253,4 +253,5 @@ | |||||||
|     <string name="history_empty">Historikken er tom</string> |     <string name="history_empty">Historikken er tom</string> | ||||||
|     <string name="history_cleared">Historikk tømt</string> |     <string name="history_cleared">Historikk tømt</string> | ||||||
|     <string name="item_deleted">Element slettet</string> |     <string name="item_deleted">Element slettet</string> | ||||||
|  | <string name="delete_item_search_history">Ønsker du å slette dette elementet fra søkehistorikken?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
|     <string name="storage_permission_denied">Toegang tot opslag geweigerd</string> |     <string name="storage_permission_denied">Toegang tot opslag geweigerd</string> | ||||||
|     <string name="start">Begin</string> |     <string name="start">Begin</string> | ||||||
|     <string name="pause">Pauzeren</string> |     <string name="pause">Pauzeren</string> | ||||||
|     <string name="view">Bekijken</string> |     <string name="view">Afspelen</string> | ||||||
|     <string name="delete">Verwijderen</string> |     <string name="delete">Verwijderen</string> | ||||||
|     <string name="checksum">Controlesom</string> |     <string name="checksum">Controlesom</string> | ||||||
|  |  | ||||||
| @@ -260,4 +260,5 @@ te openen in pop-upmodus</string> | |||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Item verwijderd</string> |     <string name="item_deleted">Item verwijderd</string> | ||||||
|  | <string name="delete_item_search_history">Wil je dit item uit je geschiedenis verwijderen?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|     <string name="what_device_headline">Informações:</string> |     <string name="what_device_headline">Informações:</string> | ||||||
|     <string name="webm_description">WebM — formato aberto</string> |     <string name="webm_description">WebM — formato aberto</string> | ||||||
|     <string name="view_count_text">%1$s visualizações</string> |     <string name="view_count_text">%1$s visualizações</string> | ||||||
|     <string name="view">Ver</string> |     <string name="view">Reproduzir</string> | ||||||
|     <string name="video_is_age_restricted">Vídeo com restrição de idade. Permissão para vídeos com essa restrição podem ser feitas no menu configurações.</string> |     <string name="video_is_age_restricted">Vídeo com restrição de idade. Permissão para vídeos com essa restrição podem ser feitas no menu configurações.</string> | ||||||
|     <string name="video">Vídeo</string> |     <string name="video">Vídeo</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">Reproduzir o vídeo automaticamente quando o NewPipe for aberto a partir de outro app</string> |     <string name="autoplay_by_calling_app_summary">Reproduzir o vídeo automaticamente quando o NewPipe for aberto a partir de outro app</string> | ||||||
| @@ -238,7 +238,8 @@ abrir em modo popup</string> | |||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Item excluído</string> |     <string name="item_deleted">Item excluído</string> | ||||||
| <string name="settings_category_player_title">Player</string> | <string name="settings_category_player_title">Reprodutor</string> | ||||||
|     <string name="empty_subscription_feed_subtitle">Não há nada aqui</string> |     <string name="empty_subscription_feed_subtitle">Não há nada aqui</string> | ||||||
|  |  | ||||||
|     </resources> |     <string name="delete_item_search_history">Deseja apagar este item do seu histórico de busca?</string> | ||||||
|  | </resources> | ||||||
|   | |||||||
| @@ -21,18 +21,18 @@ | |||||||
|     <string name="download_path_dialog_title">Introduza o caminho para os vídeos</string> |     <string name="download_path_dialog_title">Introduza o caminho para os vídeos</string> | ||||||
|     <string name="default_resolution_title">Resolução padrão</string> |     <string name="default_resolution_title">Resolução padrão</string> | ||||||
|     <string name="play_with_kodi_title">Reproduzir no Kodi</string> |     <string name="play_with_kodi_title">Reproduzir no Kodi</string> | ||||||
|     <string name="kore_not_found">Aplicação não encontrada. Instalar o Kore?</string> |     <string name="kore_not_found">Aplicação Kore não encontrada. Quer instalá-la?</string> | ||||||
|     <string name="show_play_with_kodi_title">Mostrar opção \"Reproduzir no Kodi\"</string> |     <string name="show_play_with_kodi_title">Mostrar opção \"Reproduzir no Kodi\"</string> | ||||||
|     <string name="show_play_with_kodi_summary">Mostra uma opção para reproduzir o vídeo no Kodi</string> |     <string name="show_play_with_kodi_summary">Mostra uma opção para reproduzir o vídeo no Kodi</string> | ||||||
|     <string name="play_audio">Áudio</string> |     <string name="play_audio">Áudio</string> | ||||||
|     <string name="default_audio_format_title">Formato áudio padrão</string> |     <string name="default_audio_format_title">Formato áudio padrão</string> | ||||||
|     <string name="webm_description">WebM — formato livre</string> |     <string name="webm_description">WebM — formato livre</string> | ||||||
|     <string name="m4a_description">m4a — melhor qualidade</string> |     <string name="m4a_description">M4A — melhor qualidade</string> | ||||||
|     <string name="download_dialog_title">Transferir</string> |     <string name="download_dialog_title">Transferir</string> | ||||||
|     <string name="next_video_title">Vídeo seguinte</string> |     <string name="next_video_title">Vídeo seguinte</string> | ||||||
|     <string name="show_next_and_similar_title">Mostrar vídeos seguintes e semelhantes</string> |     <string name="show_next_and_similar_title">Mostrar vídeos seguintes e semelhantes</string> | ||||||
|     <string name="url_not_supported_toast">URL não suportado</string> |     <string name="url_not_supported_toast">URL não suportado</string> | ||||||
|     <string name="search_language_title">Idioma preferido do conteúdo</string> |     <string name="search_language_title">Idioma predefinido do conteúdo</string> | ||||||
|     <string name="settings_category_video_audio_title">Vídeo e áudio</string> |     <string name="settings_category_video_audio_title">Vídeo e áudio</string> | ||||||
|  |  | ||||||
|     <string name="list_thumbnail_view_description">Miniatura de vídeos</string> |     <string name="list_thumbnail_view_description">Miniatura de vídeos</string> | ||||||
| @@ -60,19 +60,19 @@ | |||||||
|     <string name="info_dir_created">Diretório \'%1$s\' criado com sucesso</string> |     <string name="info_dir_created">Diretório \'%1$s\' criado com sucesso</string> | ||||||
|     <string name="general_error">Erro</string> |     <string name="general_error">Erro</string> | ||||||
|     <string name="could_not_load_thumbnails">Incapaz de carregar todas as miniaturas</string> |     <string name="could_not_load_thumbnails">Incapaz de carregar todas as miniaturas</string> | ||||||
|     <string name="youtube_signature_decryption_error">Incapaz de decodificar a assinatura do vídeo.</string> |     <string name="youtube_signature_decryption_error">Incapaz de decodificar a assinatura do vídeo</string> | ||||||
|     <string name="parsing_error">Incapaz de processar o sítio web.</string> |     <string name="parsing_error">Incapaz de processar o site</string> | ||||||
|     <string name="content_not_available">Conteúdo não disponível.</string> |     <string name="content_not_available">Conteúdo não disponível</string> | ||||||
|     <string name="blocked_by_gema">Bloqueado pela GEMA.</string> |     <string name="blocked_by_gema">Bloqueado pela GEMA</string> | ||||||
|  |  | ||||||
|     <string name="content">Conteúdo</string> |     <string name="content">Conteúdo</string> | ||||||
|     <string name="show_age_restricted_content_title">Restringir conteúdo por idade</string> |     <string name="show_age_restricted_content_title">Restringir conteúdo por idade</string> | ||||||
|     <string name="video_is_age_restricted">O vídeo está restrito por idade. Ative a restrição de vídeos por idade nas definições.</string> |     <string name="video_is_age_restricted">Vídeo com restrição de idade. É possível permitir este material através das Definições.</string> | ||||||
|  |  | ||||||
|     <string name="light_parsing_error">Não foi possível processar o sítio web.</string> |     <string name="light_parsing_error">Não foi possível processar totalmente o site</string> | ||||||
|     <string name="could_not_setup_download_menu">Não foi possível configurar o menu de transferências.</string> |     <string name="could_not_setup_download_menu">Não foi possível configurar o menu de transferências</string> | ||||||
|     <string name="live_streams_not_supported">Esta é uma EMISSÃO EM DIRETO. Estas emissões ainda não são suportadas.</string> |     <string name="live_streams_not_supported">Esta é uma EMISSÃO EM DIRETO, as quais ainda não são suportadas.</string> | ||||||
|     <string name="could_not_get_stream">Não foi possível obter a emissão.</string> |     <string name="could_not_get_stream">Não foi possível obter a emissão</string> | ||||||
|     <string name="sorry_string">Desculpe, isto não deveria ter acontecido.</string> |     <string name="sorry_string">Desculpe, isto não deveria ter acontecido.</string> | ||||||
|     <string name="error_report_button_text">Reportar erro por e-mail</string> |     <string name="error_report_button_text">Reportar erro por e-mail</string> | ||||||
|     <string name="error_snackbar_message">Ocorreram alguns erros.</string> |     <string name="error_snackbar_message">Ocorreram alguns erros.</string> | ||||||
| @@ -86,9 +86,9 @@ | |||||||
|     <string name="video">Vídeo</string> |     <string name="video">Vídeo</string> | ||||||
|     <string name="audio">Áudio</string> |     <string name="audio">Áudio</string> | ||||||
|     <string name="retry">Tentar novamente</string> |     <string name="retry">Tentar novamente</string> | ||||||
|     <string name="storage_permission_denied">Não foi concedida permissão para aceder ao armazenamento</string> |     <string name="storage_permission_denied">Permissão para aceder ao armazenamento foi negada</string> | ||||||
|     <string name="main_bg_subtitle">Toque para iniciar a pesquisa</string> |     <string name="main_bg_subtitle">Toque para iniciar a pesquisa</string> | ||||||
|     <string name="autoplay_by_calling_app_title">Reproduzir se invocado por outra aplicação</string> |     <string name="autoplay_by_calling_app_title">Reprodução automática</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">Reproduzir vídeo automaticamente se o NewPipe for invocado por outra aplicação</string> |     <string name="autoplay_by_calling_app_summary">Reproduzir vídeo automaticamente se o NewPipe for invocado por outra aplicação</string> | ||||||
|     <string name="duration_live">direto</string> |     <string name="duration_live">direto</string> | ||||||
|  |  | ||||||
| @@ -101,7 +101,7 @@ | |||||||
|  |  | ||||||
|     <string name="start">Iniciar</string> |     <string name="start">Iniciar</string> | ||||||
|     <string name="pause">Pausa</string> |     <string name="pause">Pausa</string> | ||||||
|     <string name="view">Ver</string> |     <string name="view">Reproduzir</string> | ||||||
|     <string name="delete">Apagar</string> |     <string name="delete">Apagar</string> | ||||||
|     <string name="checksum">Checksum</string> |     <string name="checksum">Checksum</string> | ||||||
|  |  | ||||||
| @@ -115,8 +115,8 @@ | |||||||
|     <string name="msg_url_malform">URL inválido ou Internet não disponível</string> |     <string name="msg_url_malform">URL inválido ou Internet não disponível</string> | ||||||
|     <string name="msg_running_detail">Toque para detalhes</string> |     <string name="msg_running_detail">Toque para detalhes</string> | ||||||
|     <string name="msg_wait">Por favor aguarde…</string> |     <string name="msg_wait">Por favor aguarde…</string> | ||||||
|     <string name="msg_copied">Copiado para a área de transferência.</string> |     <string name="msg_copied">Copiado para a área de transferência</string> | ||||||
|     <string name="no_available_dir">Por favor selecione um diretório disponível.</string> |     <string name="no_available_dir">Por favor selecione a pasta para as descargas</string> | ||||||
|  |  | ||||||
|     <string name="finish">OK</string> |     <string name="finish">OK</string> | ||||||
|     <string name="msg_threads">Processos</string> |     <string name="msg_threads">Processos</string> | ||||||
| @@ -145,11 +145,11 @@ o modo “popup“</string> | |||||||
|     <string name="reCaptcha_title">Desafio reCAPTCHA</string> |     <string name="reCaptcha_title">Desafio reCAPTCHA</string> | ||||||
|     <string name="recaptcha_request_toast">Desafio reCAPTCHA solicitado</string> |     <string name="recaptcha_request_toast">Desafio reCAPTCHA solicitado</string> | ||||||
|  |  | ||||||
|     <string name="popup_mode_share_menu_title">Modo popup de NewPipe</string> |     <string name="popup_mode_share_menu_title">Modo popup do NewPipe</string> | ||||||
|  |  | ||||||
|     <string name="popup_playing_toast">Reproduzir em modo de popup</string> |     <string name="popup_playing_toast">Reproduzir em modo de popup</string> | ||||||
|     <string name="use_old_player_title">Usar reprodutor antigo</string> |     <string name="use_old_player_title">Usar reprodutor antigo</string> | ||||||
|     <string name="use_old_player_summary">Versão antiga no reprodutor Mediaframework.</string> |     <string name="use_old_player_summary">Versão antiga do reprodutor Mediaframework</string> | ||||||
|     <string name="default_video_format_title">Formato de vídeo preferido</string> |     <string name="default_video_format_title">Formato de vídeo preferido</string> | ||||||
|     <string name="disabled">Desativado</string> |     <string name="disabled">Desativado</string> | ||||||
|  |  | ||||||
| @@ -186,10 +186,74 @@ o modo “popup“</string> | |||||||
|     <string name="tab_about">Sobre</string> |     <string name="tab_about">Sobre</string> | ||||||
|     <string name="tab_contributors">Colaboradores</string> |     <string name="tab_contributors">Colaboradores</string> | ||||||
|     <string name="tab_licenses">Licenças</string> |     <string name="tab_licenses">Licenças</string> | ||||||
|     <string name="app_description">Aplicação leve, simples e grátis de Youtube para Android.</string> |     <string name="app_description">Aplicação leve, simples e grátis de YouTube para Android.</string> | ||||||
|     <string name="view_on_github">Ver no Github</string> |     <string name="view_on_github">Ver no GitHub</string> | ||||||
|     <string name="app_license_title">Licença do NewPipe</string> |     <string name="app_license_title">Licença do NewPipe</string> | ||||||
|     <string name="contribution_encouragement">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!</string> |     <string name="contribution_encouragement">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!</string> | ||||||
|     <string name="read_full_license">Ler licença</string> |     <string name="read_full_license">Ler licença</string> | ||||||
|     <string name="contribution_title">Contribuição</string> |     <string name="contribution_title">Contribuição</string> | ||||||
|  | <string name="subscribe_button_title">Subscrever</string> | ||||||
|  |     <string name="subscribed_button_title">Subscrito</string> | ||||||
|  |     <string name="channel_unsubscribed">Canal não subscrito</string> | ||||||
|  |     <string name="subscription_change_failed">Incapaz de alterar a subscrição</string> | ||||||
|  |     <string name="subscription_update_failed">Incapaz de atualizar a subscrição</string> | ||||||
|  |  | ||||||
|  |     <string name="tab_main">Principal</string> | ||||||
|  |     <string name="tab_subscriptions">Subscrições</string> | ||||||
|  |  | ||||||
|  |     <string name="fragment_whats_new">O que há de novo</string> | ||||||
|  |  | ||||||
|  |     <string name="enable_search_history_title">Histórico de Pesquisa</string> | ||||||
|  |     <string name="enable_search_history_summary">Armazenar termos de pesquisa localmente</string> | ||||||
|  |     <string name="enable_watch_history_title">Histórico</string> | ||||||
|  |     <string name="enable_watch_history_summary">Armazenar histórico de vídeos assistidos</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_title">Retomar ao ganhar foco</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_summary">Continuar reprodução após interrupções (ex. chamadas)</string> | ||||||
|  |     <string name="settings_category_player_title">Reprodutor</string> | ||||||
|  |     <string name="settings_category_player_behavior_title">Comportamento</string> | ||||||
|  |     <string name="settings_category_history_title">Histórico</string> | ||||||
|  |     <string name="playlist">Lista de Reprodução</string> | ||||||
|  |     <string name="undo">Desfazer</string> | ||||||
|  |  | ||||||
|  |     <string name="notification_channel_name">Notificação do NewPipe</string> | ||||||
|  |     <string name="notification_channel_description">Notificações do NewPipe e para reprodutores pop-up</string> | ||||||
|  |  | ||||||
|  |     <string name="search_no_results">Sem resultados</string> | ||||||
|  |     <string name="empty_subscription_feed_subtitle">Aqui não há nada para ver</string> | ||||||
|  |  | ||||||
|  |     <string name="no_subscribers">Sem subscritores</string> | ||||||
|  |     <plurals name="subscribers"> | ||||||
|  | 	<item quantity="one">%s subscrito</item> | ||||||
|  | 	<item quantity="other">%s subscritos</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_views">Sem visualizações</string> | ||||||
|  |     <plurals name="views"> | ||||||
|  | 	<item quantity="one">%s visualização</item> | ||||||
|  | 	<item quantity="other">%s visualizações</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_videos">Sem vídeos</string> | ||||||
|  |     <plurals name="videos"> | ||||||
|  | 	<item quantity="one">%s vídeo</item> | ||||||
|  | 	<item quantity="other">%s vídeos</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="settings_category_downloads_title">Download</string> | ||||||
|  |     <string name="settings_file_charset_title">Caracteres permitidos em nomes de ficheiros</string> | ||||||
|  |     <string name="settings_file_replacement_character_summary">Caracteres inválidos são substituídos por este valor</string> | ||||||
|  |     <string name="settings_file_replacement_character_title">Carácter de substituição</string> | ||||||
|  |  | ||||||
|  |     <string name="charset_letters_and_digits">Letras e dígitos</string> | ||||||
|  |     <string name="charset_most_special_characters">Caracteres especiais</string> | ||||||
|  |  | ||||||
|  |     <string name="title_activity_history">Histórico</string> | ||||||
|  |     <string name="title_history_search">Pesquisado</string> | ||||||
|  |     <string name="title_history_view">Visto</string> | ||||||
|  |     <string name="history_disabled">Histórico está desativado</string> | ||||||
|  |     <string name="action_history">Histórico</string> | ||||||
|  |     <string name="history_empty">O histórico está vazio</string> | ||||||
|  |     <string name="history_cleared">Histórico eliminado</string> | ||||||
|  |     <string name="item_deleted">Item apagado</string> | ||||||
|  | <string name="delete_item_search_history">Deseja apagar este item do histórico de pesquisa?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -96,10 +96,10 @@ | |||||||
|     <string name="live_streams_not_supported">Это прямая трансляция, они пока не поддерживаются.</string> |     <string name="live_streams_not_supported">Это прямая трансляция, они пока не поддерживаются.</string> | ||||||
|     <string name="could_not_load_image">Не удалось загрузить изображение</string> |     <string name="could_not_load_image">Не удалось загрузить изображение</string> | ||||||
|     <string name="app_ui_crash">"Падение приложения/пользовательского интерфейса "</string> |     <string name="app_ui_crash">"Падение приложения/пользовательского интерфейса "</string> | ||||||
|     <string name="sorry_string">Простите, такое не должно было произойти.</string> |     <string name="sorry_string">Простите, это не должно было произойти.</string> | ||||||
|     <string name="error_report_button_text">Отправить отчёт об ошибке по электронной почте</string> |     <string name="error_report_button_text">Отправить отчёт об ошибке по электронной почте</string> | ||||||
|     <string name="error_snackbar_message">Простите, произошли ошибки.</string> |     <string name="error_snackbar_message">Простите, произошли ошибки.</string> | ||||||
|     <string name="error_snackbar_action">ОТЧЕТ</string> |     <string name="error_snackbar_action">ОТЧЁТ</string> | ||||||
|     <string name="what_device_headline">Информация:</string> |     <string name="what_device_headline">Информация:</string> | ||||||
|     <string name="what_happened_headline">Что произошло:</string> |     <string name="what_happened_headline">Что произошло:</string> | ||||||
|     <string name="error_details_headline">Детали:</string> |     <string name="error_details_headline">Детали:</string> | ||||||
| @@ -237,7 +237,7 @@ | |||||||
| 	<item quantity="one">%s подписчик</item> | 	<item quantity="one">%s подписчик</item> | ||||||
| 	<item quantity="few">%s подписчика</item> | 	<item quantity="few">%s подписчика</item> | ||||||
| 	<item quantity="many">%s подписчиков</item> | 	<item quantity="many">%s подписчиков</item> | ||||||
| 	<item quantity="other"></item> | 	<item quantity="other"/> | ||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="no_views">Нет просмотров</string> |     <string name="no_views">Нет просмотров</string> | ||||||
| @@ -245,7 +245,7 @@ | |||||||
| 	<item quantity="one">%s просмотр</item> | 	<item quantity="one">%s просмотр</item> | ||||||
| 	<item quantity="few">%s просмотра</item> | 	<item quantity="few">%s просмотра</item> | ||||||
| 	<item quantity="many">%s просмотров</item> | 	<item quantity="many">%s просмотров</item> | ||||||
| 	<item quantity="other"></item> | 	<item quantity="other"/> | ||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="no_videos">Нет видео</string> |     <string name="no_videos">Нет видео</string> | ||||||
| @@ -253,7 +253,9 @@ | |||||||
| 	<item quantity="one">%s видео</item> | 	<item quantity="one">%s видео</item> | ||||||
| 	<item quantity="few">%s видео</item> | 	<item quantity="few">%s видео</item> | ||||||
| 	<item quantity="many">%s видео</item> | 	<item quantity="many">%s видео</item> | ||||||
| 	<item quantity="other"></item> | 	<item quantity="other"/> | ||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     </resources> |     <string name="item_deleted">Элемент удалён</string> | ||||||
|  | <string name="delete_item_search_history">Удалить этот элемент из истории поиска?</string> | ||||||
|  | </resources> | ||||||
|   | |||||||
| @@ -102,7 +102,7 @@ | |||||||
|     <string name="main_bg_subtitle">Začnite z iskanjem</string> |     <string name="main_bg_subtitle">Začnite z iskanjem</string> | ||||||
|     <string name="start">Začni</string> |     <string name="start">Začni</string> | ||||||
|     <string name="pause">Premor</string> |     <string name="pause">Premor</string> | ||||||
|     <string name="view">Poglej</string> |     <string name="view">Predvajaj</string> | ||||||
|     <string name="delete">Izbriši</string> |     <string name="delete">Izbriši</string> | ||||||
|     <string name="checksum">Nadzorna vsota</string> |     <string name="checksum">Nadzorna vsota</string> | ||||||
|  |  | ||||||
| @@ -272,4 +272,5 @@ odpiranje v pojavnem načinu</string> | |||||||
| </plurals> | </plurals> | ||||||
|  |  | ||||||
|     <string name="item_deleted">Predmet je izbrisan</string> |     <string name="item_deleted">Predmet je izbrisan</string> | ||||||
|  | <string name="delete_item_search_history">Ali želite izbrisati predmet iz zgodovine iskanja?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -100,7 +100,7 @@ | |||||||
|     <string name="autoplay_by_calling_app_summary">Аутоматско пуштање видеа по позиву друге апликације</string> |     <string name="autoplay_by_calling_app_summary">Аутоматско пуштање видеа по позиву друге апликације</string> | ||||||
|     <string name="start">Почни</string> |     <string name="start">Почни</string> | ||||||
|     <string name="pause">Паузирај</string> |     <string name="pause">Паузирај</string> | ||||||
|     <string name="view">Приказ</string> |     <string name="view">Пусти</string> | ||||||
|     <string name="delete">Обриши</string> |     <string name="delete">Обриши</string> | ||||||
|     <string name="checksum">Хеш</string> |     <string name="checksum">Хеш</string> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ | |||||||
|  |  | ||||||
|     <string name="start">Başlat</string> |     <string name="start">Başlat</string> | ||||||
|     <string name="pause">Duraklat</string> |     <string name="pause">Duraklat</string> | ||||||
|     <string name="view">Görünüm</string> |     <string name="view">Oynat</string> | ||||||
|     <string name="delete">Sil</string> |     <string name="delete">Sil</string> | ||||||
|     <string name="checksum">Sağlama</string> |     <string name="checksum">Sağlama</string> | ||||||
|  |  | ||||||
| @@ -255,4 +255,5 @@ | |||||||
|     <string name="history_empty">Geçmiş boş</string> |     <string name="history_empty">Geçmiş boş</string> | ||||||
|     <string name="history_cleared">Geçmiş temizlendi</string> |     <string name="history_cleared">Geçmiş temizlendi</string> | ||||||
|     <string name="item_deleted">Öge silindi</string> |     <string name="item_deleted">Öge silindi</string> | ||||||
|  | <string name="delete_item_search_history">Bu içeriği arama geçmişinden silmek istiyor musunuz?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ | |||||||
|     <string name="show_play_with_kodi_title">顯示「用 Kodi 播放」的選項</string> |     <string name="show_play_with_kodi_title">顯示「用 Kodi 播放」的選項</string> | ||||||
|     <string name="default_audio_format_title">預設音訊格式</string> |     <string name="default_audio_format_title">預設音訊格式</string> | ||||||
|     <string name="webm_description">WebM — 開放格式</string> |     <string name="webm_description">WebM — 開放格式</string> | ||||||
|     <string name="m4a_description">m4a — 更佳畫質</string> |     <string name="m4a_description">M4A — 更佳畫質</string> | ||||||
|     <string name="theme_title">主題</string> |     <string name="theme_title">主題</string> | ||||||
|     <string name="dark_theme_title">暗色系</string> |     <string name="dark_theme_title">暗色系</string> | ||||||
|     <string name="light_theme_title">明亮色系</string> |     <string name="light_theme_title">明亮色系</string> | ||||||
| @@ -36,7 +36,7 @@ | |||||||
|     <string name="next_video_title">下一部影片</string> |     <string name="next_video_title">下一部影片</string> | ||||||
|     <string name="show_next_and_similar_title">顯示下一部和相關的影片</string> |     <string name="show_next_and_similar_title">顯示下一部和相關的影片</string> | ||||||
|     <string name="url_not_supported_toast">不支援此網址</string> |     <string name="url_not_supported_toast">不支援此網址</string> | ||||||
|     <string name="search_language_title">內容語言</string> |     <string name="search_language_title">預設內容語言</string> | ||||||
|     <string name="settings_category_video_audio_title">影片和音訊</string> |     <string name="settings_category_video_audio_title">影片和音訊</string> | ||||||
|     <string name="settings_category_appearance_title">外觀</string> |     <string name="settings_category_appearance_title">外觀</string> | ||||||
|     <string name="settings_category_other_title">其他</string> |     <string name="settings_category_other_title">其他</string> | ||||||
| @@ -62,7 +62,7 @@ | |||||||
|     <string name="use_external_video_player_summary">勾選後,部分解析度可能不會有音訊</string> |     <string name="use_external_video_player_summary">勾選後,部分解析度可能不會有音訊</string> | ||||||
|     <string name="popup_mode_share_menu_title">NewPipe 懸浮視窗模式</string> |     <string name="popup_mode_share_menu_title">NewPipe 懸浮視窗模式</string> | ||||||
|     <string name="controls_background_title">背景</string> |     <string name="controls_background_title">背景</string> | ||||||
|     <string name="autoplay_by_calling_app_title">從其他應用程式開啟時自動播放</string> |     <string name="autoplay_by_calling_app_title">自動播放</string> | ||||||
|     <string name="autoplay_by_calling_app_summary">當 NewPipe 被其他應用程式呼叫時自動播放影片</string> |     <string name="autoplay_by_calling_app_summary">當 NewPipe 被其他應用程式呼叫時自動播放影片</string> | ||||||
|     <string name="default_popup_resolution_title">懸浮視窗預設解析度</string> |     <string name="default_popup_resolution_title">懸浮視窗預設解析度</string> | ||||||
|     <string name="show_higher_resolutions_title">顯示更高的解析度</string> |     <string name="show_higher_resolutions_title">顯示更高的解析度</string> | ||||||
| @@ -97,14 +97,14 @@ | |||||||
|     <string name="popup_resizing_indicator_title">重新設定大小</string> |     <string name="popup_resizing_indicator_title">重新設定大小</string> | ||||||
|     <string name="general_error">錯誤</string> |     <string name="general_error">錯誤</string> | ||||||
|     <string name="could_not_load_thumbnails">無法載入所有縮圖</string> |     <string name="could_not_load_thumbnails">無法載入所有縮圖</string> | ||||||
|     <string name="youtube_signature_decryption_error">無法解析影片 URL 簽名。</string> |     <string name="youtube_signature_decryption_error">無法解析影片 URL 簽章</string> | ||||||
|     <string name="parsing_error">無法解析網站。</string> |     <string name="parsing_error">無法解析網站</string> | ||||||
|     <string name="light_parsing_error">無法完全解析網站。</string> |     <string name="light_parsing_error">無法完全解析網站</string> | ||||||
|     <string name="content_not_available">內容無法使用。</string> |     <string name="content_not_available">內容無法使用</string> | ||||||
|     <string name="blocked_by_gema">已被 GEMA 阻擋。</string> |     <string name="blocked_by_gema">已被 GEMA 阻擋</string> | ||||||
|     <string name="could_not_setup_download_menu">無法設定下載選單。</string> |     <string name="could_not_setup_download_menu">無法設定下載選單</string> | ||||||
|     <string name="live_streams_not_supported">尚未支援現場直播。</string> |     <string name="live_streams_not_supported">尚未支援現場直播。</string> | ||||||
|     <string name="could_not_get_stream">無法取得串流。</string> |     <string name="could_not_get_stream">無法取得串流</string> | ||||||
|     <string name="could_not_load_image">無法載入圖片</string> |     <string name="could_not_load_image">無法載入圖片</string> | ||||||
|     <string name="app_ui_crash">應用程式或 UI 已停止運作</string> |     <string name="app_ui_crash">應用程式或 UI 已停止運作</string> | ||||||
|     <string name="sorry_string">抱歉,這不應該發生的。</string> |     <string name="sorry_string">抱歉,這不應該發生的。</string> | ||||||
| @@ -126,7 +126,7 @@ | |||||||
|     <string name="retry">重試</string> |     <string name="retry">重試</string> | ||||||
|     <string name="storage_permission_denied">無法存取儲存空間</string> |     <string name="storage_permission_denied">無法存取儲存空間</string> | ||||||
|     <string name="use_old_player_title">使用舊的播放器</string> |     <string name="use_old_player_title">使用舊的播放器</string> | ||||||
|     <string name="use_old_player_summary">舊型內建媒體播放器。</string> |     <string name="use_old_player_summary">舊型內建媒體播放器</string> | ||||||
|  |  | ||||||
|  |  | ||||||
|     <string name="short_thousand">千</string> |     <string name="short_thousand">千</string> | ||||||
| @@ -135,7 +135,7 @@ | |||||||
|  |  | ||||||
|     <string name="start">開始</string> |     <string name="start">開始</string> | ||||||
|     <string name="pause">暫停</string> |     <string name="pause">暫停</string> | ||||||
|     <string name="view">檢視</string> |     <string name="view">播放</string> | ||||||
|     <string name="delete">刪除</string> |     <string name="delete">刪除</string> | ||||||
|     <string name="checksum">檢查碼</string> |     <string name="checksum">檢查碼</string> | ||||||
|  |  | ||||||
| @@ -151,8 +151,8 @@ | |||||||
|     <string name="msg_running">NewPipe 下載中</string> |     <string name="msg_running">NewPipe 下載中</string> | ||||||
|     <string name="msg_running_detail">輕觸顯示詳細資訊</string> |     <string name="msg_running_detail">輕觸顯示詳細資訊</string> | ||||||
|     <string name="msg_wait">請稍候…</string> |     <string name="msg_wait">請稍候…</string> | ||||||
|     <string name="msg_copied">已複製至剪貼簿。</string> |     <string name="msg_copied">已複製至剪貼簿</string> | ||||||
|     <string name="no_available_dir">請選擇下載資料夾。</string> |     <string name="no_available_dir">請選擇下載資料夾</string> | ||||||
|     <string name="msg_popup_permission">使用懸浮視窗模式需要此權限</string> |     <string name="msg_popup_permission">使用懸浮視窗模式需要此權限</string> | ||||||
|  |  | ||||||
|     <string name="reCaptchaActivity">reCAPTCHA 驗證</string> |     <string name="reCaptchaActivity">reCAPTCHA 驗證</string> | ||||||
| @@ -162,4 +162,82 @@ | |||||||
|     <string name="controls_popup_title">懸浮視窗</string> |     <string name="controls_popup_title">懸浮視窗</string> | ||||||
|  |  | ||||||
|     <string name="duration_live">現場直播</string> |     <string name="duration_live">現場直播</string> | ||||||
|     </resources> |     <string name="subscribe_button_title">訂閱</string> | ||||||
|  |     <string name="subscribed_button_title">已訂閱</string> | ||||||
|  |     <string name="channel_unsubscribed">已取消訂閱頻道</string> | ||||||
|  |     <string name="subscription_change_failed">無法切換訂閱</string> | ||||||
|  |     <string name="subscription_update_failed">無法更新訂閱</string> | ||||||
|  |  | ||||||
|  |     <string name="tab_main">主選單</string> | ||||||
|  |     <string name="tab_subscriptions">訂閱項目</string> | ||||||
|  |  | ||||||
|  |     <string name="fragment_whats_new">有什麼新鮮事</string> | ||||||
|  |  | ||||||
|  |     <string name="enable_search_history_title">搜尋紀錄</string> | ||||||
|  |     <string name="enable_search_history_summary">在本機儲存搜尋紀錄</string> | ||||||
|  |     <string name="enable_watch_history_title">歷史紀錄</string> | ||||||
|  |     <string name="enable_watch_history_summary">記錄觀看過的影片</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_title">在取得視窗焦點時繼續播放</string> | ||||||
|  |     <string name="resume_on_audio_focus_gain_summary">在干擾結束後繼續播放(例如有來電)</string> | ||||||
|  |     <string name="settings_category_player_title">播放器</string> | ||||||
|  |     <string name="settings_category_player_behavior_title">行為</string> | ||||||
|  |     <string name="settings_category_history_title">歷史紀錄</string> | ||||||
|  |     <string name="playlist">播放清單</string> | ||||||
|  |     <string name="undo">復原</string> | ||||||
|  |  | ||||||
|  |     <string name="notification_channel_name">NewPipe 通知</string> | ||||||
|  |     <string name="notification_channel_description">NewPipe 背景播放與懸浮模式播放器的通知</string> | ||||||
|  |  | ||||||
|  |     <string name="search_no_results">沒有結果</string> | ||||||
|  |     <string name="empty_subscription_feed_subtitle">空空如也</string> | ||||||
|  |  | ||||||
|  |     <string name="no_subscribers">無訂閱者</string> | ||||||
|  |     <plurals name="subscribers"> | ||||||
|  | 	<item quantity="other">%s 位訂閱者</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_views">無觀看次數</string> | ||||||
|  |     <plurals name="views"> | ||||||
|  | 	<item quantity="other">%s 次觀看</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="no_videos">沒有影片</string> | ||||||
|  |     <plurals name="videos"> | ||||||
|  | 	<item quantity="other">%s 部影片</item> | ||||||
|  | </plurals> | ||||||
|  |  | ||||||
|  |     <string name="settings_category_downloads_title">下載</string> | ||||||
|  |     <string name="settings_file_charset_title">檔案名稱中允許的字元</string> | ||||||
|  |     <string name="settings_file_replacement_character_summary">不符合設定的字元將會被替換為此字串</string> | ||||||
|  |     <string name="settings_file_replacement_character_title">替換為</string> | ||||||
|  |  | ||||||
|  |     <string name="charset_letters_and_digits">字母與數字</string> | ||||||
|  |     <string name="charset_most_special_characters">大部分的特殊字元</string> | ||||||
|  |  | ||||||
|  |     <string name="title_activity_about">關於 NewPipe</string> | ||||||
|  |     <string name="action_settings">設定</string> | ||||||
|  |     <string name="action_about">關於</string> | ||||||
|  |     <string name="title_licenses">第三方授權</string> | ||||||
|  |     <string name="copyright" formatted="true">© %1$s 由 %2$s 使用 %3$s 授權條款發佈</string> | ||||||
|  |     <string name="error_unable_to_load_license">無法載入授權條款</string> | ||||||
|  |     <string name="action_open_website">開啟網站</string> | ||||||
|  |     <string name="tab_about">關於</string> | ||||||
|  |     <string name="tab_contributors">貢獻者</string> | ||||||
|  |     <string name="tab_licenses">授權條款</string> | ||||||
|  |     <string name="app_description">一款在 Android 上免費輕巧的 YouTube 前端。</string> | ||||||
|  |     <string name="view_on_github">在 GitHub 上檢視</string> | ||||||
|  |     <string name="app_license_title">NewPipe 使用的授權條款</string> | ||||||
|  |     <string name="contribution_encouragement">不管你有什麼點子,翻譯、設計、程式碼整理,或者程式碼撰寫,我們永遠歡迎你來幫忙。完成的越多,NewPipe 也會更好!</string> | ||||||
|  |     <string name="read_full_license">閱讀授權條款</string> | ||||||
|  |     <string name="contribution_title">貢獻</string> | ||||||
|  |  | ||||||
|  |     <string name="title_activity_history">歷史紀錄</string> | ||||||
|  |     <string name="title_history_search">已搜尋</string> | ||||||
|  |     <string name="title_history_view">已觀看</string> | ||||||
|  |     <string name="history_disabled">歷史紀錄已被停用</string> | ||||||
|  |     <string name="action_history">歷史紀錄</string> | ||||||
|  |     <string name="history_empty">沒有歷史紀錄</string> | ||||||
|  |     <string name="history_cleared">已清除歷史紀錄</string> | ||||||
|  |     <string name="item_deleted">項目已刪除</string> | ||||||
|  |     <string name="delete_item_search_history">確定要刪除此項搜尋紀錄嗎?</string> | ||||||
|  | </resources> | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								app/src/main/res/values/ic_launcher_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <resources> | ||||||
|  |   <color name="ic_launcher_background">#CD201F</color> | ||||||
|  | </resources> | ||||||
| @@ -51,7 +51,7 @@ | |||||||
|     <string-array name="video_format_description_list" translatable="false"> |     <string-array name="video_format_description_list" translatable="false"> | ||||||
|         <item>MPEG-4</item> |         <item>MPEG-4</item> | ||||||
|         <item>WebM</item> |         <item>WebM</item> | ||||||
|         <item>3GPP</item> |         <item>3GP</item> | ||||||
|     </string-array> |     </string-array> | ||||||
|     <string-array name="video_format_values_list" translatable="false"> |     <string-array name="video_format_values_list" translatable="false"> | ||||||
|         <item>@string/video_mp4_key</item> |         <item>@string/video_mp4_key</item> | ||||||
|   | |||||||
| @@ -194,7 +194,7 @@ | |||||||
|     <!-- Missions --> |     <!-- Missions --> | ||||||
|     <string name="start">Start</string> |     <string name="start">Start</string> | ||||||
|     <string name="pause">Pause</string> |     <string name="pause">Pause</string> | ||||||
|     <string name="view">View</string> |     <string name="view">Play</string> | ||||||
|     <string name="delete">Delete</string> |     <string name="delete">Delete</string> | ||||||
|     <string name="checksum">Checksum</string> |     <string name="checksum">Checksum</string> | ||||||
|  |  | ||||||
| @@ -265,4 +265,5 @@ | |||||||
|     <string name="history_empty">The history is empty</string> |     <string name="history_empty">The history is empty</string> | ||||||
|     <string name="history_cleared">History cleared</string> |     <string name="history_cleared">History cleared</string> | ||||||
|     <string name="item_deleted">Item deleted</string> |     <string name="item_deleted">Item deleted</string> | ||||||
|  |     <string name="delete_item_search_history">Do you want to delete this item from search history?</string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -109,23 +109,47 @@ | |||||||
|         <item name="background">@color/video_overlay_color</item> |         <item name="background">@color/video_overlay_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <!-- You can also inherit from NNF_BaseTheme.Light --> |     <style name="FilePickerThemeLight" parent="NNF_BaseTheme.Light"> | ||||||
|     <style name="FilePickerTheme" parent="NNF_BaseTheme.Light"> |  | ||||||
|         <!-- Set these to match your theme --> |  | ||||||
|         <item name="colorPrimary">@color/light_youtube_primary_color</item> |         <item name="colorPrimary">@color/light_youtube_primary_color</item> | ||||||
|         <item name="colorPrimaryDark">@color/light_youtube_dark_color</item> |         <item name="colorPrimaryDark">@color/light_youtube_dark_color</item> | ||||||
|         <item name="colorAccent">@color/light_youtube_accent_color</item> |         <item name="colorAccent">@color/light_youtube_accent_color</item> | ||||||
|  |         <item name="android:background">@color/light_background_color</item> | ||||||
|  |         <item name="nnf_separator_color">@color/light_separator_color</item> | ||||||
|  |  | ||||||
|         <!-- Need to set this also to style create folder dialog --> |         <item name="alertDialogTheme">@style/FilePickerAlertDialogThemeLight</item> | ||||||
|         <item name="alertDialogTheme">@style/FilePickerAlertDialogTheme</item> |         <item name="nnf_toolbarTheme">@style/FilePickerToolbarLight</item> | ||||||
|  |  | ||||||
|         <!-- If you want to set a specific toolbar theme, do it here --> |  | ||||||
|         <!-- <item name="nnf_toolbarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> --> |  | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <style name="FilePickerAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert"> |     <style name="FilePickerAlertDialogThemeLight" parent="Theme.AppCompat.Dialog.Alert"> | ||||||
|         <item name="colorPrimary">@color/light_youtube_primary_color</item> |         <item name="colorPrimary">@color/light_youtube_primary_color</item> | ||||||
|         <item name="colorPrimaryDark">@color/light_youtube_dark_color</item> |         <item name="colorPrimaryDark">@color/light_youtube_dark_color</item> | ||||||
|         <item name="colorAccent">@color/light_youtube_accent_color</item> |         <item name="colorAccent">@color/light_youtube_accent_color</item> | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|  |     <style name="FilePickerToolbarLight" parent="ThemeOverlay.AppCompat.Dark.ActionBar"> | ||||||
|  |         <item name="android:background">@color/light_youtube_primary_color</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
|  |     <style name="FilePickerThemeDark" parent="FilePickerThemeLight"> | ||||||
|  |         <item name="colorPrimary">@color/dark_youtube_primary_color</item> | ||||||
|  |         <item name="colorPrimaryDark">@color/dark_youtube_dark_color</item> | ||||||
|  |         <item name="colorAccent">@color/dark_youtube_accent_color</item> | ||||||
|  |         <item name="android:background">@color/dark_background_color</item> | ||||||
|  |         <item name="android:textColorPrimary">@color/dark_youtube_accent_color</item> | ||||||
|  |         <item name="nnf_separator_color">@color/black_separator_color</item> | ||||||
|  |  | ||||||
|  |         <item name="alertDialogTheme">@style/FilePickerAlertDialogThemeDark</item> | ||||||
|  |         <item name="nnf_toolbarTheme">@style/FilePickerToolbarDark</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
|  |     <style name="FilePickerAlertDialogThemeDark" parent="Theme.AppCompat.Dialog.Alert"> | ||||||
|  |         <item name="colorPrimary">@color/dark_youtube_primary_color</item> | ||||||
|  |         <item name="colorPrimaryDark">@color/dark_youtube_dark_color</item> | ||||||
|  |         <item name="colorAccent">@color/dark_youtube_accent_color</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
|  |     <style name="FilePickerToolbarDark" parent="ThemeOverlay.AppCompat.Dark.ActionBar"> | ||||||
|  |         <item name="android:background">@color/dark_youtube_primary_color</item> | ||||||
|  |     </style> | ||||||
|  |  | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package us.shandian.giga.get.get; | package us.shandian.giga.get; | ||||||
| 
 | 
 | ||||||
| import org.junit.Ignore; | import org.junit.Ignore; | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| @@ -153,4 +153,34 @@ public class DownloadManagerImplTest { | |||||||
|         assertSame(missions.get(1), downloadManager.getMission(1)); |         assertSame(missions.get(1), downloadManager.getMission(1)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Test | ||||||
|  |     public void sortByTimestamp() throws Exception { | ||||||
|  |         ArrayList<DownloadMission> downloadMissions = new ArrayList<>(); | ||||||
|  |         DownloadMission mission = new DownloadMission(); | ||||||
|  |         mission.timestamp = 0; | ||||||
|  | 
 | ||||||
|  |         DownloadMission mission1 = new DownloadMission(); | ||||||
|  |         mission1.timestamp = Integer.MAX_VALUE + 1L; | ||||||
|  | 
 | ||||||
|  |         DownloadMission mission2 = new DownloadMission(); | ||||||
|  |         mission2.timestamp = 2L * Integer.MAX_VALUE ; | ||||||
|  | 
 | ||||||
|  |         DownloadMission mission3 = new DownloadMission(); | ||||||
|  |         mission3.timestamp = 2L * Integer.MAX_VALUE + 5L; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         downloadMissions.add(mission3); | ||||||
|  |         downloadMissions.add(mission1); | ||||||
|  |         downloadMissions.add(mission2); | ||||||
|  |         downloadMissions.add(mission); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         DownloadManagerImpl.sortByTimestamp(downloadMissions); | ||||||
|  | 
 | ||||||
|  |         assertEquals(mission, downloadMissions.get(0)); | ||||||
|  |         assertEquals(mission1, downloadMissions.get(1)); | ||||||
|  |         assertEquals(mission2, downloadMissions.get(2)); | ||||||
|  |         assertEquals(mission3, downloadMissions.get(3)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
							
								
								
									
										546
									
								
								assets/BETA_new_pipe_icon_5.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,546 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||||
|  | <svg | ||||||
|  |    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||||
|  |    xmlns:cc="http://creativecommons.org/ns#" | ||||||
|  |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||||
|  |    xmlns:svg="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns="http://www.w3.org/2000/svg" | ||||||
|  |    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||||
|  |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||||
|  |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||||
|  |    version="1.1" | ||||||
|  |    id="svg2" | ||||||
|  |    viewBox="0 0 192 192" | ||||||
|  |    height="204.8" | ||||||
|  |    width="204.8" | ||||||
|  |    inkscape:version="0.91 r13725" | ||||||
|  |    sodipodi:docname="BETA_new_pipe_icon_5.svg" | ||||||
|  |    inkscape:export-filename="/home/the-scrabi/Projects/NewPipe/assets/new_pipe_icon_5.png" | ||||||
|  |    inkscape:export-xdpi="120" | ||||||
|  |    inkscape:export-ydpi="120"> | ||||||
|  |   <sodipodi:namedview | ||||||
|  |      pagecolor="#ffffff" | ||||||
|  |      bordercolor="#666666" | ||||||
|  |      borderopacity="1" | ||||||
|  |      objecttolerance="10" | ||||||
|  |      gridtolerance="10" | ||||||
|  |      guidetolerance="10" | ||||||
|  |      inkscape:pageopacity="0" | ||||||
|  |      inkscape:pageshadow="2" | ||||||
|  |      inkscape:window-width="1366" | ||||||
|  |      inkscape:window-height="713" | ||||||
|  |      id="namedview4149" | ||||||
|  |      showgrid="false" | ||||||
|  |      inkscape:zoom="2.5673112" | ||||||
|  |      inkscape:cx="105.05076" | ||||||
|  |      inkscape:cy="98.81914" | ||||||
|  |      inkscape:window-x="0" | ||||||
|  |      inkscape:window-y="0" | ||||||
|  |      inkscape:window-maximized="1" | ||||||
|  |      inkscape:current-layer="svg2" /> | ||||||
|  |   <defs | ||||||
|  |      id="defs4"> | ||||||
|  |     <linearGradient | ||||||
|  |        inkscape:collect="always" | ||||||
|  |        id="linearGradient4447"> | ||||||
|  |       <stop | ||||||
|  |          style="stop-color:#ffffff;stop-opacity:0.1" | ||||||
|  |          offset="0" | ||||||
|  |          id="stop4449" /> | ||||||
|  |       <stop | ||||||
|  |          style="stop-color:#ffffff;stop-opacity:0" | ||||||
|  |          offset="1" | ||||||
|  |          id="stop4451" /> | ||||||
|  |     </linearGradient> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4454" | ||||||
|  |        width="1.4" | ||||||
|  |        height="1.4" | ||||||
|  |        x="-0.2" | ||||||
|  |        y="-0.2"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.427451" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4456" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4458" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="10.9" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4460" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="7" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4462" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4464" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4777"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4779" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4781" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="5.82011" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4783" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5.6" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4785" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="fbSourceGraphic" | ||||||
|  |          id="feComposite4787" /> | ||||||
|  |       <feColorMatrix | ||||||
|  |          result="fbSourceGraphicAlpha" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0" | ||||||
|  |          id="feColorMatrix4789" /> | ||||||
|  |       <feFlood | ||||||
|  |          id="feFlood4791" | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          in="fbSourceGraphic" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4793" | ||||||
|  |          in2="fbSourceGraphic" | ||||||
|  |          in="flood" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          id="feGaussianBlur4795" | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="5.8" | ||||||
|  |          result="blur" /> | ||||||
|  |       <feOffset | ||||||
|  |          id="feOffset4797" | ||||||
|  |          dx="0" | ||||||
|  |          dy="5.6" | ||||||
|  |          result="offset" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4799" | ||||||
|  |          in2="offset" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4885"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4887" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4889" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.9" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4891" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="2.54709" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4893" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="fbSourceGraphic" | ||||||
|  |          id="feComposite4895" /> | ||||||
|  |       <feColorMatrix | ||||||
|  |          result="fbSourceGraphicAlpha" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0" | ||||||
|  |          id="feColorMatrix4897" /> | ||||||
|  |       <feFlood | ||||||
|  |          id="feFlood4899" | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          in="fbSourceGraphic" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4901" | ||||||
|  |          in2="fbSourceGraphic" | ||||||
|  |          in="flood" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          id="feGaussianBlur4903" | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.9" | ||||||
|  |          result="blur" /> | ||||||
|  |       <feOffset | ||||||
|  |          id="feOffset4905" | ||||||
|  |          dx="0" | ||||||
|  |          dy="2.5" | ||||||
|  |          result="offset" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4907" | ||||||
|  |          in2="offset" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4257"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4259" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4261" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.9" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4263" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5.02645" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4265" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="fbSourceGraphic" | ||||||
|  |          id="feComposite4267" /> | ||||||
|  |       <feColorMatrix | ||||||
|  |          result="fbSourceGraphicAlpha" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0" | ||||||
|  |          id="feColorMatrix4269" /> | ||||||
|  |       <feFlood | ||||||
|  |          id="feFlood4271" | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          in="fbSourceGraphic" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4273" | ||||||
|  |          in2="fbSourceGraphic" | ||||||
|  |          in="flood" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          id="feGaussianBlur4275" | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.9" | ||||||
|  |          result="blur" /> | ||||||
|  |       <feOffset | ||||||
|  |          id="feOffset4277" | ||||||
|  |          dx="0" | ||||||
|  |          dy="5" | ||||||
|  |          result="offset" /> | ||||||
|  |       <feComposite | ||||||
|  |          id="feComposite4279" | ||||||
|  |          in2="offset" | ||||||
|  |          in="fbSourceGraphic" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4192"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4194" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4196" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.7" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4198" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4200" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4202" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4349"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4351" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4353" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="7.2" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4355" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4357" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4359" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4361"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4363" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4365" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="5.3" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4367" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4369" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4371" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4481"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.498039" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4483" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4485" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="5" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4487" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="5" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4489" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4491" /> | ||||||
|  |     </filter> | ||||||
|  |     <filter | ||||||
|  |        style="color-interpolation-filters:sRGB" | ||||||
|  |        inkscape:label="Drop Shadow" | ||||||
|  |        id="filter4433"> | ||||||
|  |       <feFlood | ||||||
|  |          flood-opacity="0.2" | ||||||
|  |          flood-color="rgb(0,0,0)" | ||||||
|  |          result="flood" | ||||||
|  |          id="feFlood4435" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="flood" | ||||||
|  |          in2="SourceGraphic" | ||||||
|  |          operator="in" | ||||||
|  |          result="composite1" | ||||||
|  |          id="feComposite4437" /> | ||||||
|  |       <feGaussianBlur | ||||||
|  |          in="composite1" | ||||||
|  |          stdDeviation="4" | ||||||
|  |          result="blur" | ||||||
|  |          id="feGaussianBlur4439" /> | ||||||
|  |       <feOffset | ||||||
|  |          dx="0" | ||||||
|  |          dy="4" | ||||||
|  |          result="offset" | ||||||
|  |          id="feOffset4441" /> | ||||||
|  |       <feComposite | ||||||
|  |          in="SourceGraphic" | ||||||
|  |          in2="offset" | ||||||
|  |          operator="over" | ||||||
|  |          result="composite2" | ||||||
|  |          id="feComposite4443" /> | ||||||
|  |     </filter> | ||||||
|  |     <radialGradient | ||||||
|  |        inkscape:collect="always" | ||||||
|  |        xlink:href="#linearGradient4447" | ||||||
|  |        id="radialGradient4453" | ||||||
|  |        cx="0.56012386" | ||||||
|  |        cy="0.35701406" | ||||||
|  |        fx="0.56012386" | ||||||
|  |        fy="0.35701406" | ||||||
|  |        r="88" | ||||||
|  |        gradientUnits="userSpaceOnUse" | ||||||
|  |        gradientTransform="matrix(0.00132321,2.1587518,-2.1815784,0.00133718,1.1131155,-0.39216099)" /> | ||||||
|  |   </defs> | ||||||
|  |   <metadata | ||||||
|  |      id="metadata7"> | ||||||
|  |     <rdf:RDF> | ||||||
|  |       <cc:Work | ||||||
|  |          rdf:about=""> | ||||||
|  |         <dc:format>image/svg+xml</dc:format> | ||||||
|  |         <dc:type | ||||||
|  |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||||
|  |         <dc:title /> | ||||||
|  |       </cc:Work> | ||||||
|  |     </rdf:RDF> | ||||||
|  |   </metadata> | ||||||
|  |   <path | ||||||
|  |      style="opacity:1;fill:#ff7575;fill-opacity:1;stroke:none;stroke-width:17.10300064;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||||
|  |      id="path4144-9" | ||||||
|  |      r="88" | ||||||
|  |      cy="104" | ||||||
|  |      cx="88" | ||||||
|  |      d="" | ||||||
|  |      inkscape:connector-curvature="0" /> | ||||||
|  |   <circle | ||||||
|  |      style="fill:#ff5252;fill-opacity:1;stroke:none;stroke-width:17.10300064;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||||
|  |      id="path4144-6" | ||||||
|  |      cx="88" | ||||||
|  |      cy="104" | ||||||
|  |      r="0" /> | ||||||
|  |   <circle | ||||||
|  |      style="opacity:1;fill:#ff7575;fill-opacity:1;stroke:none;stroke-width:17.10300064;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4433)" | ||||||
|  |      id="path4144-67" | ||||||
|  |      cx="96" | ||||||
|  |      cy="96" | ||||||
|  |      r="88" /> | ||||||
|  |   <path | ||||||
|  |      style="opacity:1;fill:#cc4242;fill-opacity:1;stroke:none;stroke-width:17.10300064;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4433)" | ||||||
|  |      id="path4144-5" | ||||||
|  |      sodipodi:type="arc" | ||||||
|  |      sodipodi:cx="96" | ||||||
|  |      sodipodi:cy="96" | ||||||
|  |      sodipodi:rx="88" | ||||||
|  |      sodipodi:ry="88" | ||||||
|  |      sodipodi:start="0" | ||||||
|  |      sodipodi:end="3.1387981" | ||||||
|  |      sodipodi:open="true" | ||||||
|  |      d="M 184,96 A 88,88 0 0 1 96.12296,183.99991 88,88 0 0 1 8.0003436,96.24592" /> | ||||||
|  |   <ellipse | ||||||
|  |      style="fill:#cd201f;fill-opacity:1;stroke:none;stroke-width:17.10300064;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4433)" | ||||||
|  |      id="path4144" | ||||||
|  |      cx="96" | ||||||
|  |      cy="96" | ||||||
|  |      rx="88" | ||||||
|  |      ry="87" /> | ||||||
|  |   <path | ||||||
|  |      style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter4433)" | ||||||
|  |      d="M 62.03771,154.84774 79.885207,143.47161 80.258503,68.06594 126.18321,95.333 92.950545,114.49592 v 21.92836 L 161.9848,95.333 61.944386,36.538494 Z" | ||||||
|  |      id="path4234" | ||||||
|  |      inkscape:connector-curvature="0" | ||||||
|  |      sodipodi:nodetypes="ccccccccc" /> | ||||||
|  |   <path | ||||||
|  |      style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||||
|  |      id="path4445-5" | ||||||
|  |      sodipodi:type="arc" | ||||||
|  |      sodipodi:cx="96.001564" | ||||||
|  |      sodipodi:cy="96.021866" | ||||||
|  |      sodipodi:rx="88" | ||||||
|  |      sodipodi:ry="88" | ||||||
|  |      sodipodi:start="0.082013054" | ||||||
|  |      sodipodi:end="1.9854818" | ||||||
|  |      sodipodi:open="true" | ||||||
|  |      d="m 183.70578,103.23093 a 88,88 0 0 1 -42.68319,68.40249 88,88 0 0 1 -80.476409,4.92984" /> | ||||||
|  |   <text | ||||||
|  |      xml:space="preserve" | ||||||
|  |      style="font-style:normal;font-weight:normal;font-size:37.49999619px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#cd201f;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||||
|  |      x="-4.6590595" | ||||||
|  |      y="208.67332" | ||||||
|  |      id="text3415" | ||||||
|  |      sodipodi:linespacing="125%" | ||||||
|  |      transform="matrix(0.86357335,-0.50422323,0.50422323,0.86357335,0,0)"><tspan | ||||||
|  |        sodipodi:role="line" | ||||||
|  |        id="tspan3417" | ||||||
|  |        x="-4.6590595" | ||||||
|  |        y="208.67332" | ||||||
|  |        style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.29687309px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#cd201f;fill-opacity:1">BETA</tspan></text> | ||||||
|  |   <circle | ||||||
|  |      style="opacity:1;fill:url(#radialGradient4453);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||||
|  |      id="path4445" | ||||||
|  |      cx="95.978127" | ||||||
|  |      cy="96.021866" | ||||||
|  |      r="88" /> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 16 KiB | 
| Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/bountysource_qr_code.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
| @@ -3,6 +3,7 @@ | |||||||
| buildscript { | buildscript { | ||||||
|     repositories { |     repositories { | ||||||
|         jcenter() |         jcenter() | ||||||
|  |         google() | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'com.android.tools.build:gradle:2.3.3' |         classpath 'com.android.tools.build:gradle:2.3.3' | ||||||
| @@ -16,7 +17,7 @@ allprojects { | |||||||
|     repositories { |     repositories { | ||||||
|         jcenter() |         jcenter() | ||||||
|         maven { url 'https://jitpack.io' } |         maven { url 'https://jitpack.io' } | ||||||
|         maven { url 'https://maven.google.com' } |         google() | ||||||
|         maven { url 'https://clojars.org/repo' } |         maven { url 'https://clojars.org/repo' } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										3
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,6 +1,5 @@ | |||||||
| #Tue Mar 07 14:05:42 CET 2017 |  | ||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | 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 | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -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 | # Attempt to set APP_HOME | ||||||
| # Resolve links: $0 may be a link | # Resolve links: $0 may be a link | ||||||
| PRG="$0" | PRG="$0" | ||||||
| @@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do | |||||||
|     fi |     fi | ||||||
| done | done | ||||||
| SAVED="`pwd`" | SAVED="`pwd`" | ||||||
| cd "`dirname \"$PRG\"`/" >&- | cd "`dirname \"$PRG\"`/" >/dev/null | ||||||
| APP_HOME="`pwd -P`" | 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 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | ||||||
|  |  | ||||||
| @@ -90,7 +89,7 @@ location of your Java installation." | |||||||
| fi | fi | ||||||
|  |  | ||||||
| # Increase the maximum file descriptors if we can. | # 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` |     MAX_FD_LIMIT=`ulimit -H -n` | ||||||
|     if [ $? -eq 0 ] ; then |     if [ $? -eq 0 ] ; then | ||||||
|         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | ||||||
| @@ -114,6 +113,7 @@ fi | |||||||
| if $cygwin ; then | if $cygwin ; then | ||||||
|     APP_HOME=`cygpath --path --mixed "$APP_HOME"` |     APP_HOME=`cygpath --path --mixed "$APP_HOME"` | ||||||
|     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | ||||||
|  |     JAVACMD=`cygpath --unix "$JAVACMD"` | ||||||
|  |  | ||||||
|     # We build the pattern for arguments to be converted via cygpath |     # We build the pattern for arguments to be converted via cygpath | ||||||
|     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | ||||||
| @@ -154,11 +154,19 @@ if $cygwin ; then | |||||||
|     esac |     esac | ||||||
| fi | fi | ||||||
|  |  | ||||||
| # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules | # Escape application args | ||||||
| function splitJvmOpts() { | save () { | ||||||
|     JVM_OPTS=("$@") |     for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done | ||||||
|  |     echo " " | ||||||
| } | } | ||||||
| eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS | APP_ARGS=$(save "$@") | ||||||
| JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" |  | ||||||
|  |  | ||||||
| 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" "$@" | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -8,14 +8,14 @@ | |||||||
| @rem Set local scope for the variables with windows NT shell | @rem Set local scope for the variables with windows NT shell | ||||||
| if "%OS%"=="Windows_NT" setlocal | 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 | set DIRNAME=%~dp0 | ||||||
| if "%DIRNAME%" == "" set DIRNAME=. | if "%DIRNAME%" == "" set DIRNAME=. | ||||||
| set APP_BASE_NAME=%~n0 | set APP_BASE_NAME=%~n0 | ||||||
| set APP_HOME=%DIRNAME% | 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 | @rem Find java.exe | ||||||
| if defined JAVA_HOME goto findJavaFromJavaHome | if defined JAVA_HOME goto findJavaFromJavaHome | ||||||
|  |  | ||||||
| @@ -46,10 +46,9 @@ echo location of your Java installation. | |||||||
| goto fail | goto fail | ||||||
|  |  | ||||||
| :init | :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 not "%OS%" == "Windows_NT" goto win9xME_args | ||||||
| if "%@eval[2+2]" == "4" goto 4NT_args |  | ||||||
|  |  | ||||||
| :win9xME_args | :win9xME_args | ||||||
| @rem Slurp the command line arguments. | @rem Slurp the command line arguments. | ||||||
| @@ -60,11 +59,6 @@ set _SKIP=2 | |||||||
| if "x%~1" == "x" goto execute | if "x%~1" == "x" goto execute | ||||||
|  |  | ||||||
| set CMD_LINE_ARGS=%* | set CMD_LINE_ARGS=%* | ||||||
| goto execute |  | ||||||
|  |  | ||||||
| :4NT_args |  | ||||||
| @rem Get arguments from the 4NT Shell from JP Software |  | ||||||
| set CMD_LINE_ARGS=%$ |  | ||||||
|  |  | ||||||
| :execute | :execute | ||||||
| @rem Setup the command line | @rem Setup the command line | ||||||
|   | |||||||
| Before Width: | Height: | Size: 302 KiB | 
| Before Width: | Height: | Size: 324 KiB | 
| Before Width: | Height: | Size: 399 KiB | 
| Before Width: | Height: | Size: 185 KiB | 
| Before Width: | Height: | Size: 241 KiB | 
| Before Width: | Height: | Size: 1.0 MiB | 
| Before Width: | Height: | Size: 865 KiB | 
| Before Width: | Height: | Size: 134 KiB | 
| Before Width: | Height: | Size: 476 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 112 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_10.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 40 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 110 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 263 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 101 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 122 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_6.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 50 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_7.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 88 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 44 KiB | 
							
								
								
									
										
											BIN
										
									
								
								screenshots/shot_9.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 74 KiB | 
 Christian Schabesberger
					Christian Schabesberger