Merge pull request #1825 from TeamNewPipe/release_0.14.2

Release 0.14.2
This commit is contained in:
Christian Schabesberger 2018-10-27 09:58:56 +02:00 committed by GitHub
commit fba0a8036b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
204 changed files with 4632 additions and 1473 deletions

View file

@ -5,13 +5,13 @@ android:
components: components:
# The BuildTools version used by NewPipe # The BuildTools version used by NewPipe
- tools - tools
- build-tools-27.0.3 - build-tools-28.0.3
# The SDK version used to compile NewPipe # The SDK version used to compile NewPipe
- android-27 - android-28
before_install: before_install:
- yes | sdkmanager "platforms;android-27" - yes | sdkmanager "platforms;android-28"
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses: licenses:

View file

@ -1,21 +1,22 @@
<p align="center"><a href="https://newpipe.schabi.org"><img src="assets/new_pipe_icon_5.png" width="150"/></a></p> <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> <h2 align="center"><b>NewPipe</b></h2>
<h4 align="center">A free lightweight YouTube frontend for Android.</h4> <h4 align="center">A libre lightweight streaming 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://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"> <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://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://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><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://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="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> <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>
<a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"/></a> <a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
</p> </p>
<hr /> <hr>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p> <p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<p align="center"><a href="https://newpipe.schabi.org">Website</a> &bull; <a href="https://newpipe.schabi.org/blog/">Blog</a> &bull; <a href="https://newpipe.schabi.org/press/">Press</a></p> <p align="center"><a href="https://newpipe.schabi.org">Website</a> &bull; <a href="https://newpipe.schabi.org/blog/">Blog</a> &bull; <a href="https://newpipe.schabi.org/press/">Press</a></p>
<hr /> <hr>
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
<b>WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.</b>
## Screenshots ## Screenshots
@ -29,46 +30,48 @@ WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png)
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
## Description ## Description
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without Google Services installed. Also, you don't need a YouTube account to use NewPipe, and it's FLOSS. NewPipe does not use any Google framework libraries, nor the YouTube API. Websites are only parsed to fetch required info, so this app can be used on devices without Google services installed. Also, you don't need a YouTube account to use NewPipe, which is copylefted libre software.
### Features ### Features
* Search videos * Search videos
* Display general information about a video * Display general info about videos
* Watch YouTube videos * Watch YouTube videos
* Listen to YouTube videos * Listen to YouTube videos
* Popup mode (floating player) * Popup mode (floating player)
* Select the streaming player to watch the video with * Select streaming player to watch video with
* Download videos * Download videos
* Download audio only * Download audio 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/Block age restricted material * Watch/Block age restricted material
* Display general information about channels * Display general info 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 * View history
* Subscribe to channels * Subscribe to channels
* Search history * Search history
* Search/Watch Playlists * Search/watch playlists
* Watch as queues Playlists * Watch as enqueued playlists
* Queuing videos * Enqueue videos
* Local playlists * Local playlists
* Subtitles * Subtitles
* Multi-service support (eg. SoundCloud in NewPipe Beta) * Multi-service support (e.g. SoundCloud \[beta\])
* Livestream support
### Coming Features ### Coming Features
* Livestream support
* Cast to UPnP and Cast * Cast to UPnP and Cast
* Show comments * Show comments
* ... and many more * and many more
## Contribution ## Contribution
Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome. Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome.
@ -77,26 +80,31 @@ 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 ## Donate
If you like NewPipe we'd be happy about a donation. You can either donate via Bitcoin, Bountysource or Liberapay. For further information about donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate). If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate).
<table> <table>
<tr> <tr>
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin" /></td> <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><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR code" width="100px"></td>
<td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td> <td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td>
</tr> </tr>
<tr> <tr>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" /></a></td> <td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" ></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visit NewPipe at liberapay.com" width="100px"/></a></td> <td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visit NewPipe at liberapay.com" width="100px"></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Donate via Liberapay" height="35px" /></a></td> <td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Donate via Liberapay" height="35px"></a></td>
</tr> </tr>
<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" alt="Bountysource" width="190px" /></a></td> <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" alt="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"><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> <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> </tr>
</table> </table>
## Privacy Policy
The NewPipe project aims to provide a private, anonymous experience for using media web services.
Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or comment in our blog. You can find the document [here](https://newpipe.schabi.org/legal/privacy/).
## License ## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)

View file

@ -1,15 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 27 compileSdkVersion 28
buildToolsVersion '27.0.3' buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
applicationId "org.schabi.newpipe" applicationId "org.schabi.newpipe"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 27 targetSdkVersion 28
versionCode 68 versionCode 69
versionName "0.14.1" versionName "0.14.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -22,7 +22,6 @@ android {
} }
debug { debug {
multiDexEnabled true multiDexEnabled true
debuggable true debuggable true
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
} }
@ -41,62 +40,61 @@ android {
} }
ext { ext {
supportLibVersion = '27.1.1' supportLibVersion = '28.0.0'
exoPlayerLibVersion = '2.8.2' exoPlayerLibVersion = '2.8.4' //2.9.0
roomDbLibVersion = '1.1.1' roomDbLibVersion = '1.1.1'
leakCanaryLibVersion = '1.5.4' leakCanaryLibVersion = '1.5.4' //1.6.1
okHttpLibVersion = '3.10.0' okHttpLibVersion = '3.11.0'
icepickLibVersion = '3.2.0' icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0' stethoLibVersion = '1.5.0'
} }
dependencies { dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') { androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', {
exclude module: 'support-annotations' exclude module: 'support-annotations'
} })
implementation 'com.github.TeamNewPipe:NewPipeExtractor:66c3c3f45241d4b0c909' implementation 'com.github.TeamNewPipe:NewPipeExtractor:32d316330c26'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.8.9' testImplementation 'org.mockito:mockito-core:2.23.0'
implementation "com.android.support:appcompat-v7:$supportLibVersion" implementation "com.android.support:appcompat-v7:${supportLibVersion}"
implementation "com.android.support:support-v4:$supportLibVersion" implementation "com.android.support:support-v4:${supportLibVersion}"
implementation "com.android.support:design:$supportLibVersion" implementation "com.android.support:design:${supportLibVersion}"
implementation "com.android.support:recyclerview-v7:$supportLibVersion" implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
implementation "com.android.support:preference-v14:$supportLibVersion" implementation "com.android.support:preference-v14:${supportLibVersion}"
implementation "com.android.support:cardview-v7:${supportLibVersion}"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'ch.acra:acra:4.9.2' implementation 'ch.acra:acra:4.9.2' //4.11
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
implementation 'de.hdodenhof:circleimageview:2.2.0' implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1' implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1'
implementation 'com.nononsenseapps:filepicker:4.2.1' implementation 'com.nononsenseapps:filepicker:4.2.1'
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerLibVersion" implementation "com.google.android.exoplayer:exoplayer:${exoPlayerLibVersion}"
implementation "com.google.android.exoplayer:extension-mediasession:$exoPlayerLibVersion" implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerLibVersion}"
debugImplementation "com.facebook.stetho:stetho:$stethoLibVersion" debugImplementation "com.facebook.stetho:stetho:${stethoLibVersion}"
debugImplementation "com.facebook.stetho:stetho-urlconnection:$stethoLibVersion" debugImplementation "com.facebook.stetho:stetho-urlconnection:${stethoLibVersion}"
debugImplementation 'com.android.support:multidex:1.0.3' debugImplementation 'com.android.support:multidex:1.0.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.14' implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
implementation "android.arch.persistence.room:runtime:$roomDbLibVersion" implementation "android.arch.persistence.room:runtime:${roomDbLibVersion}"
implementation "android.arch.persistence.room:rxjava2:$roomDbLibVersion" implementation "android.arch.persistence.room:rxjava2:${roomDbLibVersion}"
annotationProcessor "android.arch.persistence.room:compiler:$roomDbLibVersion" annotationProcessor "android.arch.persistence.room:compiler:${roomDbLibVersion}"
implementation "frankiesardo:icepick:$icepickLibVersion" implementation "frankiesardo:icepick:${icepickLibVersion}"
annotationProcessor "frankiesardo:icepick-processor:$icepickLibVersion" annotationProcessor "frankiesardo:icepick-processor:${icepickLibVersion}"
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion" debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryLibVersion}"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryLibVersion}"
implementation "com.squareup.okhttp3:okhttp:${okHttpLibVersion}"
implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion" debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoLibVersion}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:cardview-v7:27.1.1'
} }

View file

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application <application
android:name=".App" android:name=".App"
@ -238,4 +239,4 @@
android:name=".RouterActivity$FetcherService" android:name=".RouterActivity$FetcherService"
android:exported="false"/> android:exported="false"/>
</application> </application>
</manifest> </manifest>

View file

@ -5,6 +5,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
@ -21,6 +22,7 @@ import org.acra.config.ConfigurationBuilder;
import org.acra.sender.ReportSenderFactory; import org.acra.sender.ReportSenderFactory;
import org.schabi.newpipe.extractor.Downloader; import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.report.AcraReportSenderFactory; import org.schabi.newpipe.report.AcraReportSenderFactory;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
@ -65,7 +67,8 @@ public class App extends Application {
private RefWatcher refWatcher; private RefWatcher refWatcher;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static final Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses = new Class[]{AcraReportSenderFactory.class}; private static final Class<? extends ReportSenderFactory>[]
reportSenderFactoryClasses = new Class[]{AcraReportSenderFactory.class};
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {
@ -88,7 +91,8 @@ public class App extends Application {
// Initialize settings first because others inits can use its values // Initialize settings first because others inits can use its values
SettingsActivity.initSettings(this); SettingsActivity.initSettings(this);
NewPipe.init(getDownloader()); NewPipe.init(getDownloader(),
org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(this));
StateSaver.init(this); StateSaver.init(this);
initNotificationChannel(); initNotificationChannel();
@ -106,7 +110,7 @@ public class App extends Application {
// https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling // https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
@Override @Override
public void accept(@NonNull Throwable throwable) throws Exception { public void accept(@NonNull Throwable throwable) {
Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : " + Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : " +
"throwable = [" + throwable.getClass().getName() + "]"); "throwable = [" + throwable.getClass().getName() + "]");
@ -180,7 +184,11 @@ public class App extends Application {
ACRA.init(this, acraConfig); ACRA.init(this, acraConfig);
} catch (ACRAConfigurationException ace) { } catch (ACRAConfigurationException ace) {
ace.printStackTrace(); ace.printStackTrace();
ErrorActivity.reportError(this, ace, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", ErrorActivity.reportError(this,
ace,
null,
null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not initialize ACRA crash report", R.string.app_ui_crash)); "Could not initialize ACRA crash report", R.string.app_ui_crash));
} }
} }
@ -200,7 +208,8 @@ public class App extends Application {
NotificationChannel mChannel = new NotificationChannel(id, name, importance); NotificationChannel mChannel = new NotificationChannel(id, name, importance);
mChannel.setDescription(description); mChannel.setDescription(description);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel); mNotificationManager.createNotificationChannel(mChannel);
} }

View file

@ -12,14 +12,12 @@ import android.view.View;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher; import com.squareup.leakcanary.RefWatcher;
import org.schabi.newpipe.report.UserAction;
import icepick.Icepick; import icepick.Icepick;
import icepick.State; import icepick.State;
public abstract class BaseFragment extends Fragment { public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected boolean DEBUG = MainActivity.DEBUG; protected final boolean DEBUG = MainActivity.DEBUG;
protected AppCompatActivity activity; protected AppCompatActivity activity;
public static final ImageLoader imageLoader = ImageLoader.getInstance(); public static final ImageLoader imageLoader = ImageLoader.getInstance();

View file

@ -4,6 +4,7 @@ import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -43,7 +44,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
private static Downloader instance; private static Downloader instance;
private String mCookies; private String mCookies;
private OkHttpClient client; private final OkHttpClient client;
private Downloader(OkHttpClient.Builder builder) { private Downloader(OkHttpClient.Builder builder) {
this.client = builder this.client = builder
@ -103,13 +104,13 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
* but set the HTTP header field "Accept-Language" to the supplied string. * but set the HTTP header field "Accept-Language" to the supplied string.
* *
* @param siteUrl the URL of the text file to return the contents of * @param siteUrl the URL of the text file to return the contents of
* @param language the language (usually a 2-character code) to set as the preferred language * @param localization the language and country (usually a 2-character code) to set
* @return the contents of the specified text file * @return the contents of the specified text file
*/ */
@Override @Override
public String download(String siteUrl, String language) throws IOException, ReCaptchaException { public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException {
Map<String, String> requestProperties = new HashMap<>(); Map<String, String> requestProperties = new HashMap<>();
requestProperties.put("Accept-Language", language); requestProperties.put("Accept-Language", localization.getLanguage());
return download(siteUrl, requestProperties); return download(siteUrl, requestProperties);
} }

View file

@ -23,7 +23,6 @@ package org.schabi.newpipe;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -66,8 +65,6 @@ import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import static org.schabi.newpipe.extractor.InfoItem.InfoType.PLAYLIST;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity"; private static final String TAG = "MainActivity";
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");

View file

@ -85,7 +85,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
} }
private class ReCaptchaWebViewClient extends WebViewClient { private class ReCaptchaWebViewClient extends WebViewClient {
private Activity context; private final Activity context;
private String mCookies; private String mCookies;
ReCaptchaWebViewClient(Activity ctx) { ReCaptchaWebViewClient(Activity ctx) {

View file

@ -1,7 +1,6 @@
package org.schabi.newpipe; package org.schabi.newpipe;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.FragmentManager;
import android.app.IntentService; import android.app.IntentService;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -13,7 +12,6 @@ import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
@ -38,7 +36,6 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -51,14 +48,12 @@ import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Observer;
import icepick.Icepick; import icepick.Icepick;
import icepick.State; import icepick.State;
@ -86,7 +81,7 @@ public class RouterActivity extends AppCompatActivity {
protected int selectedPreviously = -1; protected int selectedPreviously = -1;
protected String currentUrl; protected String currentUrl;
protected CompositeDisposable disposables = new CompositeDisposable(); protected final CompositeDisposable disposables = new CompositeDisposable();
private boolean selectionIsDownload = false; private boolean selectionIsDownload = false;
@ -184,12 +179,16 @@ public class RouterActivity extends AppCompatActivity {
if (selectedChoiceKey.equals(alwaysAskKey)) { if (selectedChoiceKey.equals(alwaysAskKey)) {
final List<AdapterChoiceItem> choices = getChoicesForService(currentService, currentLinkType); final List<AdapterChoiceItem> choices = getChoicesForService(currentService, currentLinkType);
if (choices.size() == 1) { switch (choices.size()) {
handleChoice(choices.get(0).key); case 1:
} else if (choices.size() == 0) { handleChoice(choices.get(0).key);
handleChoice(showInfoKey); break;
} else { case 0:
showDialog(choices); handleChoice(showInfoKey);
break;
default:
showDialog(choices);
break;
} }
} else if (selectedChoiceKey.equals(showInfoKey)) { } else if (selectedChoiceKey.equals(showInfoKey)) {
handleChoice(showInfoKey); handleChoice(showInfoKey);

View file

@ -4,7 +4,6 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.webkit.WebView; import android.webkit.WebView;
@ -17,7 +16,7 @@ import java.lang.ref.WeakReference;
public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> { public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
WeakReference<Activity> weakReference; final WeakReference<Activity> weakReference;
private License license; private License license;
public LicenseFragmentHelper(@Nullable Activity activity) { public LicenseFragmentHelper(@Nullable Activity activity) {
@ -78,18 +77,18 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
throw new NullPointerException("license is null"); throw new NullPointerException("license is null");
} }
String licenseContent = ""; StringBuilder licenseContent = new StringBuilder();
String webViewData; String webViewData;
try { try {
BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8")); BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8"));
String str; String str;
while ((str = in.readLine()) != null) { while ((str = in.readLine()) != null) {
licenseContent += str; licenseContent.append(str);
} }
in.close(); in.close();
// split the HTML file and insert the stylesheet into the HEAD of the file // split the HTML file and insert the stylesheet into the HEAD of the file
String[] insert = licenseContent.split("</head>"); String[] insert = licenseContent.toString().split("</head>");
webViewData = insert[0] + "<style type=\"text/css\">" webViewData = insert[0] + "<style type=\"text/css\">"
+ getLicenseStylesheet(context) + "</style></head>" + getLicenseStylesheet(context) + "</style></head>"
+ insert[1]; + insert[1];

View file

@ -30,7 +30,7 @@ public interface BasicDAO<Entity> {
/* Deletes */ /* Deletes */
@Delete @Delete
int delete(final Entity entity); void delete(final Entity entity);
@Delete @Delete
int delete(final Collection<Entity> entities); int delete(final Collection<Entity> entities);
@ -42,5 +42,5 @@ public interface BasicDAO<Entity> {
int update(final Entity entity); int update(final Entity entity);
@Update @Update
int update(final Collection<Entity> entities); void update(final Collection<Entity> entities);
} }

View file

@ -4,7 +4,6 @@ import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import java.util.List; import java.util.List;

View file

@ -5,7 +5,6 @@ import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.database.history.model.StreamHistoryEntity; import org.schabi.newpipe.database.history.model.StreamHistoryEntity;

View file

@ -2,7 +2,6 @@ package org.schabi.newpipe.database.playlist.dao;
import android.arch.persistence.room.Dao; import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import android.arch.persistence.room.Transaction;
import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistEntity; import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
@ -12,7 +11,6 @@ import java.util.List;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
@Dao @Dao

View file

@ -8,7 +8,6 @@ import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import java.util.List; import java.util.List;

View file

@ -5,8 +5,6 @@ import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index; import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey; import android.arch.persistence.room.PrimaryKey;
import java.util.Date;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;

View file

@ -6,7 +6,6 @@ import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index; import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey; import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;

View file

@ -10,7 +10,6 @@ import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.database.history.model.StreamHistoryEntity; import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -23,7 +22,6 @@ import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_SERVI
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@Dao @Dao
public abstract class StreamDAO implements BasicDAO<StreamEntity> { public abstract class StreamDAO implements BasicDAO<StreamEntity> {

View file

@ -28,11 +28,11 @@ public class DeleteDownloadManager {
private static final String KEY_STATE = "delete_manager_state"; private static final String KEY_STATE = "delete_manager_state";
private View mView; private final View mView;
private HashSet<String> mPendingMap; private final HashSet<String> mPendingMap;
private List<Disposable> mDisposableList; private final List<Disposable> mDisposableList;
private DownloadManager mDownloadManager; private DownloadManager mDownloadManager;
private PublishSubject<DownloadMission> publishSubject = PublishSubject.create(); private final PublishSubject<DownloadMission> publishSubject = PublishSubject.create();
DeleteDownloadManager(Activity activity) { DeleteDownloadManager(Activity activity) {
mPendingMap = new HashSet<>(); mPendingMap = new HashSet<>();

View file

@ -55,7 +55,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
private StreamItemAdapter<AudioStream> audioStreamsAdapter; private StreamItemAdapter<AudioStream> audioStreamsAdapter;
private StreamItemAdapter<VideoStream> videoStreamsAdapter; private StreamItemAdapter<VideoStream> videoStreamsAdapter;
private CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
private EditText nameEditText; private EditText nameEditText;
private Spinner streamsSpinner; private Spinner streamsSpinner;

View file

@ -32,7 +32,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import icepick.State; import icepick.State;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;

View file

@ -2,7 +2,6 @@ package org.schabi.newpipe.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;

View file

@ -3,9 +3,9 @@ package org.schabi.newpipe.fragments.detail;
import java.io.Serializable; import java.io.Serializable;
class StackItem implements Serializable { class StackItem implements Serializable {
private int serviceId; private final int serviceId;
private String title; private String title;
private String url; private final String url;
StackItem(int serviceId, String url, String title) { StackItem(int serviceId, String url, String title) {
this.serviceId = serviceId; this.serviceId = serviceId;

View file

@ -33,12 +33,14 @@ import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -64,19 +66,17 @@ import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity; import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
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.Constants;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
@ -87,6 +87,8 @@ import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable; import java.io.Serializable;
@ -154,6 +156,7 @@ public class VideoDetailFragment
private View videoTitleRoot; private View videoTitleRoot;
private TextView videoTitleTextView; private TextView videoTitleTextView;
@Nullable
private ImageView videoTitleToggleArrow; private ImageView videoTitleToggleArrow;
private TextView videoCountView; private TextView videoCountView;
@ -415,14 +418,16 @@ public class VideoDetailFragment
} }
private void toggleTitleAndDescription() { private void toggleTitleAndDescription() {
if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) { if (videoTitleToggleArrow != null) { //it is null for tablets
videoTitleTextView.setMaxLines(1); if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) {
videoDescriptionRootLayout.setVisibility(View.GONE); videoTitleTextView.setMaxLines(1);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down); videoDescriptionRootLayout.setVisibility(View.GONE);
} else { videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoTitleTextView.setMaxLines(10); } else {
videoDescriptionRootLayout.setVisibility(View.VISIBLE); videoTitleTextView.setMaxLines(10);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_up); videoDescriptionRootLayout.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_up);
}
} }
} }
@ -622,8 +627,11 @@ public class VideoDetailFragment
relatedStreamsView.addView( relatedStreamsView.addView(
infoItemBuilder.buildView(relatedStreamsView, info.getNextVideo())); infoItemBuilder.buildView(relatedStreamsView, info.getNextVideo()));
relatedStreamsView.addView(getSeparatorView()); relatedStreamsView.addView(getSeparatorView());
relatedStreamRootLayout.setVisibility(View.VISIBLE); setRelatedStreamsVisibility(View.VISIBLE);
} else nextStreamTitle.setVisibility(View.GONE); } else {
nextStreamTitle.setVisibility(View.GONE);
setRelatedStreamsVisibility(View.GONE);
}
if (info.getRelatedStreams() != null if (info.getRelatedStreams() != null
&& !info.getRelatedStreams().isEmpty() && showRelatedStreams) { && !info.getRelatedStreams().isEmpty() && showRelatedStreams) {
@ -639,13 +647,13 @@ public class VideoDetailFragment
} }
//if (DEBUG) Log.d(TAG, "Total time " + ((System.nanoTime() - first) / 1000000L) + "ms"); //if (DEBUG) Log.d(TAG, "Total time " + ((System.nanoTime() - first) / 1000000L) + "ms");
relatedStreamRootLayout.setVisibility(View.VISIBLE); setRelatedStreamsVisibility(View.VISIBLE);
relatedStreamExpandButton.setVisibility(View.VISIBLE); relatedStreamExpandButton.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable( relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(
activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand))); activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
} else { } else {
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE); if (info.getNextVideo() == null) setRelatedStreamsVisibility(View.GONE);
relatedStreamExpandButton.setVisibility(View.GONE); relatedStreamExpandButton.setVisibility(View.GONE);
} }
} }
@ -760,7 +768,7 @@ public class VideoDetailFragment
* Stack that contains the "navigation history".<br> * Stack that contains the "navigation history".<br>
* The peek is the current video. * The peek is the current video.
*/ */
protected LinkedList<StackItem> stack = new LinkedList<>(); protected final LinkedList<StackItem> stack = new LinkedList<>();
public void clearHistory() { public void clearHistory() {
stack.clear(); stack.clear();
@ -1114,8 +1122,16 @@ public class VideoDetailFragment
animateView(videoTitleTextView, true, 0); animateView(videoTitleTextView, true, 0);
videoDescriptionRootLayout.setVisibility(View.GONE); videoDescriptionRootLayout.setVisibility(View.GONE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down); if (videoTitleToggleArrow != null) { //phone
videoTitleToggleArrow.setVisibility(View.GONE); videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoTitleToggleArrow.setVisibility(View.GONE);
} else { //tablet
final View related = (View) relatedStreamRootLayout.getParent();
//don`t need to hide it if related streams are disabled
if (related.getVisibility() == View.VISIBLE) {
related.setVisibility(View.INVISIBLE);
}
}
videoTitleRoot.setClickable(false); videoTitleRoot.setClickable(false);
imageLoader.cancelDisplayTask(thumbnailImageView); imageLoader.cancelDisplayTask(thumbnailImageView);
@ -1190,11 +1206,15 @@ public class VideoDetailFragment
detailDurationView.setVisibility(View.GONE); detailDurationView.setVisibility(View.GONE);
} }
videoTitleRoot.setClickable(true);
videoTitleToggleArrow.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoDescriptionView.setVisibility(View.GONE); videoDescriptionView.setVisibility(View.GONE);
videoDescriptionRootLayout.setVisibility(View.GONE); if (videoTitleToggleArrow != null) {
videoTitleRoot.setClickable(true);
videoTitleToggleArrow.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoDescriptionRootLayout.setVisibility(View.GONE);
} else {
videoDescriptionRootLayout.setVisibility(View.VISIBLE);
}
if (!TextUtils.isEmpty(info.getUploadDate())) { if (!TextUtils.isEmpty(info.getUploadDate())) {
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate())); videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
} }
@ -1242,6 +1262,11 @@ public class VideoDetailFragment
// Only auto play in the first open // Only auto play in the first open
autoPlayEnabled = false; autoPlayEnabled = false;
} }
final ViewParent related = relatedStreamRootLayout.getParent();
if (related instanceof ScrollView) {
((ScrollView) related).scrollTo(0, 0);
}
} }
@ -1299,4 +1324,13 @@ public class VideoDetailFragment
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema); showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
} }
private void setRelatedStreamsVisibility(int visibility) {
final ViewParent parent = relatedStreamRootLayout.getParent();
if (parent instanceof ScrollView) {
((ScrollView) parent).setVisibility(visibility);
} else {
relatedStreamRootLayout.setVisibility(visibility);
}
}
} }

View file

@ -3,10 +3,15 @@ package org.schabi.newpipe.fragments.list;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log; import android.util.Log;
@ -21,9 +26,9 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
@ -36,7 +41,7 @@ import java.util.Queue;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implements ListViewContract<I, N>, StateSaver.WriteRead { public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implements ListViewContract<I, N>, StateSaver.WriteRead, SharedPreferences.OnSharedPreferenceChangeListener {
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
@ -44,6 +49,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
protected InfoListAdapter infoListAdapter; protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList; protected RecyclerView itemsList;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// LifeCycle // LifeCycle
@ -59,12 +67,31 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
StateSaver.onDestroy(savedState); StateSaver.onDestroy(savedState);
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -119,13 +146,25 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
return new LinearLayoutManager(activity); return new LinearLayoutManager(activity);
} }
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(infoListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
@Override @Override
protected void initViews(View rootView, Bundle savedInstanceState) { protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
final boolean useGrid = isGridLayout();
itemsList = rootView.findViewById(R.id.items_list); itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(getListLayoutManager()); itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.setFooter(getListFooter()); infoListAdapter.setFooter(getListFooter());
infoListAdapter.setHeader(getListHeader()); infoListAdapter.setHeader(getListHeader());
@ -308,4 +347,22 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void handleNextItems(N result) { public void handleNextItems(N result) {
isLoading.set(false); isLoading.set(false);
} }
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
} }

View file

@ -8,9 +8,6 @@ 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.extractor.NewPipe;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import java.util.Queue; import java.util.Queue;
@ -19,7 +16,6 @@ import icepick.State;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
public abstract class BaseListInfoFragment<I extends ListInfo> public abstract class BaseListInfoFragment<I extends ListInfo>

View file

@ -68,7 +68,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> { public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
private Disposable subscribeButtonMonitor; private Disposable subscribeButtonMonitor;
private SubscriptionService subscriptionService; private SubscriptionService subscriptionService;
@ -90,8 +90,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private MenuItem menuRssButton; private MenuItem menuRssButton;
private boolean mIsVisibleToUser = false;
public static ChannelFragment getInstance(int serviceId, String url, String name) { public static ChannelFragment getInstance(int serviceId, String url, String name) {
ChannelFragment instance = new ChannelFragment(); ChannelFragment instance = new ChannelFragment();
instance.setInitialData(serviceId, url, name); instance.setInitialData(serviceId, url, name);
@ -105,7 +103,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
@Override @Override
public void setUserVisibleHint(boolean isVisibleToUser) { public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser); super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
if(activity != null if(activity != null
&& useAsFrontPage && useAsFrontPage
&& isVisibleToUser) { && isVisibleToUser) {

View file

@ -5,7 +5,6 @@ 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.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -19,8 +18,6 @@ import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo; import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
@ -131,26 +128,16 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
@Override @Override
public Single<KioskInfo> loadResult(boolean forceReload) { public Single<KioskInfo> loadResult(boolean forceReload) {
String contentCountry = PreferenceManager
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getKioskInfo(serviceId, return ExtractorHelper.getKioskInfo(serviceId,
url, url,
contentCountry,
forceReload); forceReload);
} }
@Override @Override
public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() { public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
String contentCountry = PreferenceManager
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getMoreKioskItems(serviceId, return ExtractorHelper.getMoreKioskItems(serviceId,
url, url,
currentNextPageUrl, currentNextPageUrl);
contentCountry);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////

View file

@ -20,7 +20,6 @@ import android.widget.TextView;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
import org.schabi.newpipe.App;
import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
@ -30,7 +29,6 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager;

View file

@ -122,12 +122,11 @@ public class SearchFragment
private String nextPageUrl; private String nextPageUrl;
private String contentCountry; private String contentCountry;
private boolean isSuggestionsEnabled = true; private boolean isSuggestionsEnabled = true;
private boolean isSearchHistoryEnabled = true;
private PublishSubject<String> suggestionPublisher = PublishSubject.create(); private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
private Disposable searchDisposable; private Disposable searchDisposable;
private Disposable suggestionDisposable; private Disposable suggestionDisposable;
private CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter; private SuggestionListAdapter suggestionListAdapter;
private HistoryRecordManager historyRecordManager; private HistoryRecordManager historyRecordManager;
@ -173,7 +172,7 @@ public class SearchFragment
suggestionListAdapter = new SuggestionListAdapter(activity); suggestionListAdapter = new SuggestionListAdapter(activity);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true); boolean isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled); suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
historyRecordManager = new HistoryRecordManager(context); historyRecordManager = new HistoryRecordManager(context);
@ -627,7 +626,7 @@ public class SearchFragment
} }
final Observable<List<SuggestionItem>> network = ExtractorHelper final Observable<List<SuggestionItem>> network = ExtractorHelper
.suggestionsFor(serviceId, query, contentCountry) .suggestionsFor(serviceId, query)
.toObservable() .toObservable()
.map(strings -> { .map(strings -> {
List<SuggestionItem> result = new ArrayList<>(); List<SuggestionItem> result = new ArrayList<>();
@ -727,8 +726,7 @@ public class SearchFragment
searchDisposable = ExtractorHelper.searchFor(serviceId, searchDisposable = ExtractorHelper.searchFor(serviceId,
searchString, searchString,
Arrays.asList(contentFilter), Arrays.asList(contentFilter),
sortFilter, sortFilter)
contentCountry)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false)) .doOnEvent((searchResult, throwable) -> isLoading.set(false))
@ -746,8 +744,7 @@ public class SearchFragment
searchString, searchString,
asList(contentFilter), asList(contentFilter),
sortFilter, sortFilter,
nextPageUrl, nextPageUrl)
contentCountry)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false)) .doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))

View file

@ -45,7 +45,7 @@ public class InfoItemBuilder {
private static final String TAG = InfoItemBuilder.class.toString(); private static final String TAG = InfoItemBuilder.class.toString();
private final Context context; private final Context context;
private ImageLoader imageLoader = ImageLoader.getInstance(); private final ImageLoader imageLoader = ImageLoader.getInstance();
private OnClickGesture<StreamInfoItem> onStreamSelectedListener; private OnClickGesture<StreamInfoItem> onStreamSelectedListener;
private OnClickGesture<ChannelInfoItem> onChannelSelectedListener; private OnClickGesture<ChannelInfoItem> onChannelSelectedListener;

View file

@ -5,7 +5,6 @@ import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;

View file

@ -1,6 +1,7 @@
package org.schabi.newpipe.info_list; package org.schabi.newpipe.info_list;
import android.app.Activity; import android.app.Activity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@ -12,9 +13,12 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder; import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder; import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder; import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder; import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder; import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder; import org.schabi.newpipe.util.FallbackViewHolder;
@ -52,14 +56,18 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private static final int MINI_STREAM_HOLDER_TYPE = 0x100; private static final int MINI_STREAM_HOLDER_TYPE = 0x100;
private static final int STREAM_HOLDER_TYPE = 0x101; private static final int STREAM_HOLDER_TYPE = 0x101;
private static final int GRID_STREAM_HOLDER_TYPE = 0x102;
private static final int MINI_CHANNEL_HOLDER_TYPE = 0x200; private static final int MINI_CHANNEL_HOLDER_TYPE = 0x200;
private static final int CHANNEL_HOLDER_TYPE = 0x201; private static final int CHANNEL_HOLDER_TYPE = 0x201;
private static final int GRID_CHANNEL_HOLDER_TYPE = 0x202;
private static final int MINI_PLAYLIST_HOLDER_TYPE = 0x300; private static final int MINI_PLAYLIST_HOLDER_TYPE = 0x300;
private static final int PLAYLIST_HOLDER_TYPE = 0x301; private static final int PLAYLIST_HOLDER_TYPE = 0x301;
private static final int GRID_PLAYLIST_HOLDER_TYPE = 0x302;
private final InfoItemBuilder infoItemBuilder; private final InfoItemBuilder infoItemBuilder;
private final ArrayList<InfoItem> infoItemList; private final ArrayList<InfoItem> infoItemList;
private boolean useMiniVariant = false; private boolean useMiniVariant = false;
private boolean useGridVariant = false;
private boolean showFooter = false; private boolean showFooter = false;
private View header = null; private View header = null;
private View footer = null; private View footer = null;
@ -94,6 +102,10 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
this.useMiniVariant = useMiniVariant; this.useMiniVariant = useMiniVariant;
} }
public void setGridItemVariants(boolean useGridVariant) {
this.useGridVariant = useGridVariant;
}
public void addInfoItemList(List<InfoItem> data) { public void addInfoItemList(List<InfoItem> data) {
if (data != null) { if (data != null) {
if (DEBUG) { if (DEBUG) {
@ -206,11 +218,11 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
final InfoItem item = infoItemList.get(position); final InfoItem item = infoItemList.get(position);
switch (item.getInfoType()) { switch (item.getInfoType()) {
case STREAM: case STREAM:
return useMiniVariant ? MINI_STREAM_HOLDER_TYPE : STREAM_HOLDER_TYPE; return useGridVariant ? GRID_STREAM_HOLDER_TYPE : useMiniVariant ? MINI_STREAM_HOLDER_TYPE : STREAM_HOLDER_TYPE;
case CHANNEL: case CHANNEL:
return useMiniVariant ? MINI_CHANNEL_HOLDER_TYPE : CHANNEL_HOLDER_TYPE; return useGridVariant ? GRID_CHANNEL_HOLDER_TYPE : useMiniVariant ? MINI_CHANNEL_HOLDER_TYPE : CHANNEL_HOLDER_TYPE;
case PLAYLIST: case PLAYLIST:
return useMiniVariant ? MINI_PLAYLIST_HOLDER_TYPE : PLAYLIST_HOLDER_TYPE; return useGridVariant ? GRID_PLAYLIST_HOLDER_TYPE : useMiniVariant ? MINI_PLAYLIST_HOLDER_TYPE : PLAYLIST_HOLDER_TYPE;
default: default:
Log.e(TAG, "Trollolo"); Log.e(TAG, "Trollolo");
return -1; return -1;
@ -229,14 +241,20 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return new StreamMiniInfoItemHolder(infoItemBuilder, parent); return new StreamMiniInfoItemHolder(infoItemBuilder, parent);
case STREAM_HOLDER_TYPE: case STREAM_HOLDER_TYPE:
return new StreamInfoItemHolder(infoItemBuilder, parent); return new StreamInfoItemHolder(infoItemBuilder, parent);
case GRID_STREAM_HOLDER_TYPE:
return new StreamGridInfoItemHolder(infoItemBuilder, parent);
case MINI_CHANNEL_HOLDER_TYPE: case MINI_CHANNEL_HOLDER_TYPE:
return new ChannelMiniInfoItemHolder(infoItemBuilder, parent); return new ChannelMiniInfoItemHolder(infoItemBuilder, parent);
case CHANNEL_HOLDER_TYPE: case CHANNEL_HOLDER_TYPE:
return new ChannelInfoItemHolder(infoItemBuilder, parent); return new ChannelInfoItemHolder(infoItemBuilder, parent);
case GRID_CHANNEL_HOLDER_TYPE:
return new ChannelGridInfoItemHolder(infoItemBuilder, parent);
case MINI_PLAYLIST_HOLDER_TYPE: case MINI_PLAYLIST_HOLDER_TYPE:
return new PlaylistMiniInfoItemHolder(infoItemBuilder, parent); return new PlaylistMiniInfoItemHolder(infoItemBuilder, parent);
case PLAYLIST_HOLDER_TYPE: case PLAYLIST_HOLDER_TYPE:
return new PlaylistInfoItemHolder(infoItemBuilder, parent); return new PlaylistInfoItemHolder(infoItemBuilder, parent);
case GRID_PLAYLIST_HOLDER_TYPE:
return new PlaylistGridInfoItemHolder(infoItemBuilder, parent);
default: default:
Log.e(TAG, "Trollolo"); Log.e(TAG, "Trollolo");
return new FallbackViewHolder(new View(parent.getContext())); return new FallbackViewHolder(new View(parent.getContext()));
@ -257,4 +275,14 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
((HFHolder) holder).view = footer; ((HFHolder) holder).view = footer;
} }
} }
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
return new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
final int type = getItemViewType(position);
return type == HEADER_TYPE || type == FOOTER_TYPE ? spanCount : 1;
}
};
}
} }

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class ChannelGridInfoItemHolder extends ChannelMiniInfoItemHolder {
public ChannelGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_channel_grid_item, parent);
}
}

View file

@ -47,6 +47,13 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
itemBuilder.getOnChannelSelectedListener().selected(item); itemBuilder.getOnChannelSelectedListener().selected(item);
} }
}); });
itemView.setOnLongClickListener(view -> {
if (itemBuilder.getOnChannelSelectedListener() != null) {
itemBuilder.getOnChannelSelectedListener().held(item);
}
return true;
});
} }
protected String getDetailLine(final ChannelInfoItem item) { protected String getDetailLine(final ChannelInfoItem item) {

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class PlaylistGridInfoItemHolder extends PlaylistMiniInfoItemHolder {
public PlaylistGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class StreamGridInfoItemHolder extends StreamMiniInfoItemHolder {
public StreamGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
}

View file

@ -1,8 +1,13 @@
package org.schabi.newpipe.local; package org.schabi.newpipe.local;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log; import android.util.Log;
@ -25,7 +30,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
* called and is memory efficient when in backstack. * called and is memory efficient when in backstack.
* */ * */
public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I> public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
implements ListViewContract<I, N> { implements ListViewContract<I, N>, SharedPreferences.OnSharedPreferenceChangeListener {
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
@ -36,6 +41,9 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
protected LocalItemListAdapter itemListAdapter; protected LocalItemListAdapter itemListAdapter;
protected RecyclerView itemsList; protected RecyclerView itemsList;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Lifecycle - Creation // Lifecycle - Creation
@ -45,6 +53,29 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -59,6 +90,16 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
return activity.getLayoutInflater().inflate(R.layout.pignate_footer, itemsList, false); return activity.getLayoutInflater().inflate(R.layout.pignate_footer, itemsList, false);
} }
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(itemListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
protected RecyclerView.LayoutManager getListLayoutManager() { protected RecyclerView.LayoutManager getListLayoutManager() {
return new LinearLayoutManager(activity); return new LinearLayoutManager(activity);
} }
@ -67,10 +108,13 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
protected void initViews(View rootView, Bundle savedInstanceState) { protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(getListLayoutManager());
itemListAdapter = new LocalItemListAdapter(activity); itemListAdapter = new LocalItemListAdapter(activity);
final boolean useGrid = isGridLayout();
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemListAdapter.setHeader(headerRootView = getListHeader()); itemListAdapter.setHeader(headerRootView = getListHeader());
itemListAdapter.setFooter(footerRootView = getListFooter()); itemListAdapter.setFooter(footerRootView = getListFooter());
@ -174,4 +218,22 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
resetFragment(); resetFragment();
return super.onError(exception); return super.onError(exception);
} }
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
} }

View file

@ -33,7 +33,7 @@ public class LocalItemBuilder {
private static final String TAG = LocalItemBuilder.class.toString(); private static final String TAG = LocalItemBuilder.class.toString();
private final Context context; private final Context context;
private ImageLoader imageLoader = ImageLoader.getInstance(); private final ImageLoader imageLoader = ImageLoader.getInstance();
private OnClickGesture<LocalItem> onSelectedListener; private OnClickGesture<LocalItem> onSelectedListener;

View file

@ -1,18 +1,21 @@
package org.schabi.newpipe.local; package org.schabi.newpipe.local;
import android.app.Activity; import android.app.Activity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.local.HeaderFooterHolder;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.local.holder.LocalItemHolder; import org.schabi.newpipe.local.holder.LocalItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder; import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder; import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
@ -52,14 +55,19 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
private static final int STREAM_STATISTICS_HOLDER_TYPE = 0x1000; private static final int STREAM_STATISTICS_HOLDER_TYPE = 0x1000;
private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001; private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001;
private static final int STREAM_STATISTICS_GRID_HOLDER_TYPE = 0x1002;
private static final int STREAM_PLAYLIST_GRID_HOLDER_TYPE = 0x1004;
private static final int LOCAL_PLAYLIST_HOLDER_TYPE = 0x2000; private static final int LOCAL_PLAYLIST_HOLDER_TYPE = 0x2000;
private static final int REMOTE_PLAYLIST_HOLDER_TYPE = 0x2001; private static final int REMOTE_PLAYLIST_HOLDER_TYPE = 0x2001;
private static final int LOCAL_PLAYLIST_GRID_HOLDER_TYPE = 0x2002;
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
private final LocalItemBuilder localItemBuilder; private final LocalItemBuilder localItemBuilder;
private final ArrayList<LocalItem> localItems; private final ArrayList<LocalItem> localItems;
private final DateFormat dateFormat; private final DateFormat dateFormat;
private boolean showFooter = false; private boolean showFooter = false;
private boolean useGridVariant = false;
private View header = null; private View header = null;
private View footer = null; private View footer = null;
@ -134,6 +142,10 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void setGridItemVariants(boolean useGridVariant) {
this.useGridVariant = useGridVariant;
}
public void setHeader(View header) { public void setHeader(View header) {
boolean changed = header != this.header; boolean changed = header != this.header;
this.header = header; this.header = header;
@ -195,11 +207,11 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
final LocalItem item = localItems.get(position); final LocalItem item = localItems.get(position);
switch (item.getLocalItemType()) { switch (item.getLocalItemType()) {
case PLAYLIST_LOCAL_ITEM: return LOCAL_PLAYLIST_HOLDER_TYPE; case PLAYLIST_LOCAL_ITEM: return useGridVariant ? LOCAL_PLAYLIST_GRID_HOLDER_TYPE : LOCAL_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_REMOTE_ITEM: return REMOTE_PLAYLIST_HOLDER_TYPE; case PLAYLIST_REMOTE_ITEM: return useGridVariant ? REMOTE_PLAYLIST_GRID_HOLDER_TYPE : REMOTE_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_STREAM_ITEM: return STREAM_PLAYLIST_HOLDER_TYPE; case PLAYLIST_STREAM_ITEM: return useGridVariant ? STREAM_PLAYLIST_GRID_HOLDER_TYPE : STREAM_PLAYLIST_HOLDER_TYPE;
case STATISTIC_STREAM_ITEM: return STREAM_STATISTICS_HOLDER_TYPE; case STATISTIC_STREAM_ITEM: return useGridVariant ? STREAM_STATISTICS_GRID_HOLDER_TYPE : STREAM_STATISTICS_HOLDER_TYPE;
default: default:
Log.e(TAG, "No holder type has been considered for item: [" + Log.e(TAG, "No holder type has been considered for item: [" +
item.getLocalItemType() + "]"); item.getLocalItemType() + "]");
@ -218,12 +230,20 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
return new HeaderFooterHolder(footer); return new HeaderFooterHolder(footer);
case LOCAL_PLAYLIST_HOLDER_TYPE: case LOCAL_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistItemHolder(localItemBuilder, parent); return new LocalPlaylistItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistGridItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_HOLDER_TYPE: case REMOTE_PLAYLIST_HOLDER_TYPE:
return new RemotePlaylistItemHolder(localItemBuilder, parent); return new RemotePlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_GRID_HOLDER_TYPE:
return new RemotePlaylistGridItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_HOLDER_TYPE: case STREAM_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent); return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistStreamGridItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_HOLDER_TYPE: case STREAM_STATISTICS_HOLDER_TYPE:
return new LocalStatisticStreamItemHolder(localItemBuilder, parent); return new LocalStatisticStreamItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_GRID_HOLDER_TYPE:
return new LocalStatisticStreamGridItemHolder(localItemBuilder, parent);
default: default:
Log.e(TAG, "No view type has been considered for holder: [" + type + "]"); Log.e(TAG, "No view type has been considered for holder: [" + type + "]");
return new FallbackViewHolder(new View(parent.getContext())); return new FallbackViewHolder(new View(parent.getContext()));
@ -247,4 +267,14 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
((HeaderFooterHolder) holder).view = footer; ((HeaderFooterHolder) holder).view = footer;
} }
} }
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
return new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
final int type = getItemViewType(position);
return type == HEADER_TYPE || type == FOOTER_TYPE ? spanCount : 1;
}
};
}
} }

View file

@ -1,9 +1,11 @@
package org.schabi.newpipe.local.dialog; package org.schabi.newpipe.local.dialog;
import android.app.Dialog;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.view.Window;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
@ -41,6 +43,18 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
StateSaver.onDestroy(savedState); StateSaver.onDestroy(savedState);
} }
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
//remove title
final Window window = dialog.getWindow();
if (window != null) {
window.requestFeature(Window.FEATURE_NO_TITLE);
}
return dialog;
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// State Saving // State Saving
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -58,7 +72,7 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception { public void readFrom(@NonNull Queue<Object> savedObjects) {
streamEntities = (List<StreamEntity>) savedObjects.poll(); streamEntities = (List<StreamEntity>) savedObjects.poll();
} }

View file

@ -36,8 +36,6 @@ import io.reactivex.MaybeObserver;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Void> { public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Void> {

View file

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.history; package org.schabi.newpipe.local.history;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;

View file

@ -45,7 +45,6 @@ import java.util.List;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Maybe; import io.reactivex.Maybe;
import io.reactivex.Scheduler;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;

View file

@ -21,7 +21,6 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -57,7 +56,7 @@ public class StatisticsPlaylistFragment
/* Used for independent events */ /* Used for independent events */
private Subscription databaseSubscription; private Subscription databaseSubscription;
private HistoryRecordManager recordManager; private HistoryRecordManager recordManager;
private CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
private enum StatisticSortMode { private enum StatisticSortMode {
LAST_PLAYED, LAST_PLAYED,

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistGridItemHolder extends LocalPlaylistItemHolder {
public LocalPlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View file

@ -16,6 +16,10 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
super(infoItemBuilder, parent); super(infoItemBuilder, parent);
} }
LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override @Override
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) { public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistMetadataEntry)) return; if (!(localItem instanceof PlaylistMetadataEntry)) return;

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistStreamGridItemHolder extends LocalPlaylistStreamItemHolder {
public LocalPlaylistStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_playlist_grid_item, parent); //TODO
}
}

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalStatisticStreamGridItemHolder extends LocalStatisticStreamItemHolder {
public LocalStatisticStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
}

View file

@ -1,5 +1,6 @@
package org.schabi.newpipe.local.holder; package org.schabi.newpipe.local.holder;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -42,10 +43,15 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
public final TextView itemVideoTitleView; public final TextView itemVideoTitleView;
public final TextView itemUploaderView; public final TextView itemUploaderView;
public final TextView itemDurationView; public final TextView itemDurationView;
@Nullable
public final TextView itemAdditionalDetails; public final TextView itemAdditionalDetails;
public LocalStatisticStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) { public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_item, parent); this(itemBuilder, R.layout.list_stream_item, parent);
}
LocalStatisticStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView); itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView); itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView);
@ -80,7 +86,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
itemDurationView.setVisibility(View.GONE); itemDurationView.setVisibility(View.GONE);
} }
itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat)); if (itemAdditionalDetails != null) {
itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat));
}
// Default thumbnail is shown on error, while loading and if the url is empty // Default thumbnail is shown on error, while loading and if the url is empty
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView,

View file

@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class RemotePlaylistGridItemHolder extends RemotePlaylistItemHolder {
public RemotePlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View file

@ -16,6 +16,10 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
super(infoItemBuilder, parent); super(infoItemBuilder, parent);
} }
RemotePlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override @Override
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) { public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistRemoteEntity)) return; if (!(localItem instanceof PlaylistRemoteEntity)) return;

View file

@ -459,7 +459,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
private ItemTouchHelper.SimpleCallback getItemTouchCallback() { private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
if (isGridLayout()) {
directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return new ItemTouchHelper.SimpleCallback(directions,
ItemTouchHelper.ACTION_STATE_IDLE) { ItemTouchHelper.ACTION_STATE_IDLE) {
@Override @Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize, public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,

View file

@ -1,15 +1,22 @@
package org.schabi.newpipe.local.subscription; package org.schabi.newpipe.local.subscription;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Parcelable; import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -17,6 +24,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -40,10 +48,11 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService; import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService; import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.OnClickGesture;
@ -72,7 +81,7 @@ import static org.schabi.newpipe.local.subscription.services.SubscriptionsImport
import static org.schabi.newpipe.util.AnimationUtils.animateRotation; import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEntity>> { public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEntity>> implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final int REQUEST_EXPORT_CODE = 666; private static final int REQUEST_EXPORT_CODE = 666;
private static final int REQUEST_IMPORT_CODE = 667; private static final int REQUEST_IMPORT_CODE = 667;
@ -80,8 +89,10 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
@State @State
protected Parcelable itemsListState; protected Parcelable itemsListState;
private InfoListAdapter infoListAdapter; private InfoListAdapter infoListAdapter;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
private View headerRootLayout;
private View whatsNewItemListHeader; private View whatsNewItemListHeader;
private View importExportListHeader; private View importExportListHeader;
@ -100,6 +111,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
} }
@Override @Override
@ -127,6 +140,15 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
setupBroadcastReceiver(); setupBroadcastReceiver();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
} }
@Override @Override
@ -153,9 +175,25 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
disposables = null; disposables = null;
subscriptionService = null; subscriptionService = null;
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy(); super.onDestroy();
} }
protected RecyclerView.LayoutManager getListLayoutManager() {
return new LinearLayoutManager(activity);
}
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(infoListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
/*///////////////////////////////////////////////////////////////////////// /*/////////////////////////////////////////////////////////////////////////
// Menu // Menu
/////////////////////////////////////////////////////////////////////////*/ /////////////////////////////////////////////////////////////////////////*/
@ -287,16 +325,19 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
protected void initViews(View rootView, Bundle savedInstanceState) { protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
final boolean useGrid = isGridLayout();
infoListAdapter = new InfoListAdapter(getActivity()); infoListAdapter = new InfoListAdapter(getActivity());
itemsList = rootView.findViewById(R.id.items_list); itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(new LinearLayoutManager(activity)); itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
View headerRootLayout;
infoListAdapter.setHeader(headerRootLayout = activity.getLayoutInflater().inflate(R.layout.subscription_header, itemsList, false)); infoListAdapter.setHeader(headerRootLayout = activity.getLayoutInflater().inflate(R.layout.subscription_header, itemsList, false));
whatsNewItemListHeader = headerRootLayout.findViewById(R.id.whats_new); whatsNewItemListHeader = headerRootLayout.findViewById(R.id.whats_new);
importExportListHeader = headerRootLayout.findViewById(R.id.import_export); importExportListHeader = headerRootLayout.findViewById(R.id.import_export);
importExportOptions = headerRootLayout.findViewById(R.id.import_export_options); importExportOptions = headerRootLayout.findViewById(R.id.import_export_options);
infoListAdapter.useMiniItemVariants(true); infoListAdapter.useMiniItemVariants(true);
infoListAdapter.setGridItemVariants(useGrid);
itemsList.setAdapter(infoListAdapter); itemsList.setAdapter(infoListAdapter);
setupImportFromItems(headerRootLayout.findViewById(R.id.import_from_options)); setupImportFromItems(headerRootLayout.findViewById(R.id.import_from_options));
@ -320,7 +361,7 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
super.initListeners(); super.initListeners();
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() { infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) { public void selected(ChannelInfoItem selectedItem) {
final FragmentManager fragmentManager = getFM(); final FragmentManager fragmentManager = getFM();
NavigationHelper.openChannelFragment(fragmentManager, NavigationHelper.openChannelFragment(fragmentManager,
@ -328,6 +369,11 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
selectedItem.getUrl(), selectedItem.getUrl(),
selectedItem.getName()); selectedItem.getName());
} }
public void held(ChannelInfoItem selectedItem) {
showLongTapDialog(selectedItem);
}
}); });
//noinspection ConstantConditions //noinspection ConstantConditions
@ -338,6 +384,85 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
importExportListHeader.setOnClickListener(v -> importExportOptions.switchState()); importExportListHeader.setOnClickListener(v -> importExportOptions.switchState());
} }
private void showLongTapDialog(ChannelInfoItem selectedItem) {
final Context context = getContext();
final Activity activity = getActivity();
if (context == null || context.getResources() == null || getActivity() == null) return;
final String[] commands = new String[]{
context.getResources().getString(R.string.share),
context.getResources().getString(R.string.unsubscribe)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
switch (i) {
case 0:
shareChannel(selectedItem);
break;
case 1:
deleteChannel(selectedItem);
break;
default:
break;
}
};
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
bannerView.setSelected(true);
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
titleView.setText(selectedItem.getName());
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
detailsView.setVisibility(View.GONE);
new AlertDialog.Builder(activity)
.setCustomTitle(bannerView)
.setItems(commands, actions)
.create()
.show();
}
private void shareChannel (ChannelInfoItem selectedItem) {
shareUrl(selectedItem.getName(), selectedItem.getUrl());
}
@SuppressLint("CheckResult")
private void deleteChannel (ChannelInfoItem selectedItem) {
subscriptionService.subscriptionTable()
.getSubscription(selectedItem.getServiceId(), selectedItem.getUrl())
.toObservable()
.observeOn(Schedulers.io())
.subscribe(getDeleteObserver());
Toast.makeText(activity, getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show();
}
private Observer<List<SubscriptionEntity>> getDeleteObserver(){
return new Observer<List<SubscriptionEntity>>() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(List<SubscriptionEntity> subscriptionEntities) {
subscriptionService.subscriptionTable().delete(subscriptionEntities);
}
@Override
public void onError(Throwable exception) {
SubscriptionFragment.this.onError(exception);
}
@Override
public void onComplete() { }
};
}
private void resetFragment() { private void resetFragment() {
if (disposables != null) disposables.clear(); if (disposables != null) disposables.clear();
if (infoListAdapter != null) infoListAdapter.clearStreamItemList(); if (infoListAdapter != null) infoListAdapter.clearStreamItemList();
@ -447,4 +572,22 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
R.string.general_error); R.string.general_error);
return true; return true;
} }
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
} }

View file

@ -55,10 +55,10 @@ public class SubscriptionService {
private static final int SUBSCRIPTION_DEBOUNCE_INTERVAL = 500; private static final int SUBSCRIPTION_DEBOUNCE_INTERVAL = 500;
private static final int SUBSCRIPTION_THREAD_POOL_SIZE = 4; private static final int SUBSCRIPTION_THREAD_POOL_SIZE = 4;
private AppDatabase db; private final AppDatabase db;
private Flowable<List<SubscriptionEntity>> subscription; private final Flowable<List<SubscriptionEntity>> subscription;
private Scheduler subscriptionScheduler; private final Scheduler subscriptionScheduler;
private SubscriptionService(Context context) { private SubscriptionService(Context context) {
db = NewPipeDatabase.getInstance(context.getApplicationContext()); db = NewPipeDatabase.getInstance(context.getApplicationContext());
@ -116,7 +116,7 @@ public class SubscriptionService {
public Completable updateChannelInfo(final ChannelInfo info) { public Completable updateChannelInfo(final ChannelInfo info) {
final Function<List<SubscriptionEntity>, CompletableSource> update = new Function<List<SubscriptionEntity>, CompletableSource>() { final Function<List<SubscriptionEntity>, CompletableSource> update = new Function<List<SubscriptionEntity>, CompletableSource>() {
@Override @Override
public CompletableSource apply(@NonNull List<SubscriptionEntity> subscriptionEntities) throws Exception { public CompletableSource apply(@NonNull List<SubscriptionEntity> subscriptionEntities) {
if (DEBUG) Log.d(TAG, "updateChannelInfo() called with: subscriptionEntities = [" + subscriptionEntities + "]"); if (DEBUG) Log.d(TAG, "updateChannelInfo() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscriptionEntities.size() == 1) { if (subscriptionEntities.size() == 1) {
SubscriptionEntity subscription = subscriptionEntities.get(0); SubscriptionEntity subscription = subscriptionEntities.get(0);

View file

@ -58,8 +58,8 @@ public abstract class BaseImportExportService extends Service {
protected NotificationCompat.Builder notificationBuilder; protected NotificationCompat.Builder notificationBuilder;
protected SubscriptionService subscriptionService; protected SubscriptionService subscriptionService;
protected CompositeDisposable disposables = new CompositeDisposable(); protected final CompositeDisposable disposables = new CompositeDisposable();
protected PublishProcessor<String> notificationUpdater = PublishProcessor.create(); protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create();
@Nullable @Nullable
@Override @Override
@ -90,9 +90,9 @@ public abstract class BaseImportExportService extends Service {
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500; private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
protected AtomicInteger currentProgress = new AtomicInteger(-1); protected final AtomicInteger currentProgress = new AtomicInteger(-1);
protected AtomicInteger maxProgress = new AtomicInteger(-1); protected final AtomicInteger maxProgress = new AtomicInteger(-1);
protected ImportExportEventListener eventListener = new ImportExportEventListener() { protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
@Override @Override
public void onSizeReceived(int size) { public void onSizeReceived(int size) {
maxProgress.set(size); maxProgress.set(size);
@ -187,13 +187,13 @@ public abstract class BaseImportExportService extends Service {
protected Toast toast; protected Toast toast;
protected void showToast(@StringRes int message) { protected void showToast(@StringRes int message) {
showToast(getString(message), Toast.LENGTH_SHORT); showToast(getString(message));
} }
protected void showToast(String message, int duration) { protected void showToast(String message) {
if (toast != null) toast.cancel(); if (toast != null) toast.cancel();
toast = Toast.makeText(this, message, duration); toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show(); toast.show();
} }

View file

@ -144,12 +144,16 @@ public class SubscriptionsImportService extends BaseImportExportService {
showToast(R.string.import_ongoing); showToast(R.string.import_ongoing);
Flowable<List<SubscriptionItem>> flowable = null; Flowable<List<SubscriptionItem>> flowable = null;
if (currentMode == CHANNEL_URL_MODE) { switch (currentMode) {
flowable = importFromChannelUrl(); case CHANNEL_URL_MODE:
} else if (currentMode == INPUT_STREAM_MODE) { flowable = importFromChannelUrl();
flowable = importFromInputStream(); break;
} else if (currentMode == PREVIOUS_EXPORT_MODE) { case INPUT_STREAM_MODE:
flowable = importFromPreviousExport(); flowable = importFromInputStream();
break;
case PREVIOUS_EXPORT_MODE:
flowable = importFromPreviousExport();
break;
} }
if (flowable == null) { if (flowable == null) {

View file

@ -230,7 +230,8 @@ public abstract class BasePlayer implements
int sizeBeforeAppend = playQueue.size(); int sizeBeforeAppend = playQueue.size();
playQueue.append(queue.getStreams()); playQueue.append(queue.getStreams());
if (intent.getBooleanExtra(SELECT_ON_APPEND, false) && if ((intent.getBooleanExtra(SELECT_ON_APPEND, false) ||
getCurrentState() == STATE_COMPLETED) &&
queue.getStreams().size() > 0) { queue.getStreams().size() > 0) {
playQueue.setIndex(sizeBeforeAppend); playQueue.setIndex(sizeBeforeAppend);
} }
@ -1112,7 +1113,9 @@ public abstract class BasePlayer implements
@Player.RepeatMode @Player.RepeatMode
public int getRepeatMode() { public int getRepeatMode() {
return simpleExoPlayer == null ? Player.REPEAT_MODE_OFF : simpleExoPlayer.getRepeatMode(); return simpleExoPlayer == null
? Player.REPEAT_MODE_OFF
: simpleExoPlayer.getRepeatMode();
} }
public void setRepeatMode(@Player.RepeatMode final int repeatMode) { public void setRepeatMode(@Player.RepeatMode final int repeatMode) {
@ -1178,4 +1181,8 @@ public abstract class BasePlayer implements
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos); if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
playQueue.setRecovery(queuePos, windowPos); playQueue.setRecovery(queuePos, windowPos);
} }
public boolean gotDestroyed() {
return simpleExoPlayer == null;
}
} }

View file

@ -175,6 +175,10 @@ public final class MainVideoPlayer extends AppCompatActivity
setLandscape(lastOrientationWasLandscape); setLandscape(lastOrientationWasLandscape);
} }
final int lastResizeMode = defaultPreferences.getInt(
getString(R.string.last_resize_mode), AspectRatioFrameLayout.RESIZE_MODE_FIT);
playerImpl.setResizeMode(lastResizeMode);
// Upon going in or out of multiwindow mode, isInMultiWindow will always be false, // Upon going in or out of multiwindow mode, isInMultiWindow will always be false,
// since the first onResume needs to restore the player. // since the first onResume needs to restore the player.
// Subsequent onResume calls while multiwindow mode remains the same and the player is // Subsequent onResume calls while multiwindow mode remains the same and the player is
@ -213,10 +217,9 @@ public final class MainVideoPlayer extends AppCompatActivity
if (playerImpl == null) return; if (playerImpl == null) return;
playerImpl.setRecovery(); playerImpl.setRecovery();
playerState = new PlayerState(playerImpl.getPlayQueue(), playerImpl.getRepeatMode(), if(!playerImpl.gotDestroyed()) {
playerImpl.getPlaybackSpeed(), playerImpl.getPlaybackPitch(), playerState = createPlayerState();
playerImpl.getPlaybackQuality(), playerImpl.getPlaybackSkipSilence(), }
playerImpl.isPlaying());
StateSaver.tryToSave(isChangingConfigurations(), null, outState, this); StateSaver.tryToSave(isChangingConfigurations(), null, outState, this);
} }
@ -231,6 +234,7 @@ public final class MainVideoPlayer extends AppCompatActivity
if (!isBackPressed) { if (!isBackPressed) {
playerImpl.minimize(); playerImpl.minimize();
} }
playerState = createPlayerState();
playerImpl.destroy(); playerImpl.destroy();
isInMultiWindow = false; isInMultiWindow = false;
@ -241,6 +245,13 @@ public final class MainVideoPlayer extends AppCompatActivity
// State Saving // State Saving
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private PlayerState createPlayerState() {
return new PlayerState(playerImpl.getPlayQueue(), playerImpl.getRepeatMode(),
playerImpl.getPlaybackSpeed(), playerImpl.getPlaybackPitch(),
playerImpl.getPlaybackQuality(), playerImpl.getPlaybackSkipSilence(),
playerImpl.isPlaying());
}
@Override @Override
public String generateSuffix() { public String generateSuffix() {
return "." + UUID.randomUUID().toString() + ".player"; return "." + UUID.randomUUID().toString() + ".player";
@ -705,14 +716,27 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override @Override
protected int nextResizeMode(int currentResizeMode) { protected int nextResizeMode(int currentResizeMode) {
final int newResizeMode;
switch (currentResizeMode) { switch (currentResizeMode) {
case AspectRatioFrameLayout.RESIZE_MODE_FIT: case AspectRatioFrameLayout.RESIZE_MODE_FIT:
return AspectRatioFrameLayout.RESIZE_MODE_FILL; newResizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL;
break;
case AspectRatioFrameLayout.RESIZE_MODE_FILL: case AspectRatioFrameLayout.RESIZE_MODE_FILL:
return AspectRatioFrameLayout.RESIZE_MODE_ZOOM; newResizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
break;
default: default:
return AspectRatioFrameLayout.RESIZE_MODE_FIT; newResizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
break;
} }
storeResizeMode(newResizeMode);
return newResizeMode;
}
private void storeResizeMode(@AspectRatioFrameLayout.ResizeMode int resizeMode) {
defaultPreferences.edit()
.putInt(getString(R.string.last_resize_mode), resizeMode)
.apply();
} }
@Override @Override
@ -989,12 +1013,14 @@ public final class MainVideoPlayer extends AppCompatActivity
private static final int MOVEMENT_THRESHOLD = 40; private static final int MOVEMENT_THRESHOLD = 40;
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext()); private final boolean isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(getApplicationContext());
private final boolean isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(getApplicationContext());
private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume(); private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
@Override @Override
public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) { public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
if (!isPlayerGestureEnabled) return false; if (!isVolumeGestureEnabled && !isBrightnessGestureEnabled) return false;
//noinspection PointlessBooleanExpression //noinspection PointlessBooleanExpression
if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " + if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " +
@ -1010,7 +1036,11 @@ public final class MainVideoPlayer extends AppCompatActivity
isMoving = true; isMoving = true;
if (initialEvent.getX() > playerImpl.getRootView().getWidth() / 2) { boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2;
boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea;
if (isVolumeGestureEnabled && acceptVolumeArea) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY); playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
float currentProgressPercent = float currentProgressPercent =
(float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength(); (float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
@ -1035,7 +1065,7 @@ public final class MainVideoPlayer extends AppCompatActivity
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE); playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
} }
} else { } else if (isBrightnessGestureEnabled && acceptBrightnessArea) {
playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY); playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
float currentProgressPercent = float currentProgressPercent =
(float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength(); (float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();

View file

@ -114,7 +114,6 @@ public final class PopupVideoPlayer extends Service {
private View closeOverlayView; private View closeOverlayView;
private FloatingActionButton closeOverlayButton; private FloatingActionButton closeOverlayButton;
private WindowManager.LayoutParams closeOverlayLayoutParams;
private int tossFlingVelocity; private int tossFlingVelocity;
@ -248,7 +247,7 @@ public final class PopupVideoPlayer extends Service {
final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
closeOverlayLayoutParams = new WindowManager.LayoutParams( WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
layoutParamType, layoutParamType,
flags, flags,

View file

@ -137,16 +137,16 @@ public abstract class VideoPlayer extends BasePlayer
private TextView captionTextView; private TextView captionTextView;
private ValueAnimator controlViewAnimator; private ValueAnimator controlViewAnimator;
private Handler controlsVisibilityHandler = new Handler(); private final Handler controlsVisibilityHandler = new Handler();
boolean isSomePopupMenuVisible = false; boolean isSomePopupMenuVisible = false;
private int qualityPopupMenuGroupId = 69; private final int qualityPopupMenuGroupId = 69;
private PopupMenu qualityPopupMenu; private PopupMenu qualityPopupMenu;
private int playbackSpeedPopupMenuGroupId = 79; private final int playbackSpeedPopupMenuGroupId = 79;
private PopupMenu playbackSpeedPopupMenu; private PopupMenu playbackSpeedPopupMenu;
private int captionPopupMenuGroupId = 89; private final int captionPopupMenuGroupId = 89;
private PopupMenu captionPopupMenu; private PopupMenu captionPopupMenu;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -683,12 +683,17 @@ public abstract class VideoPlayer extends BasePlayer
if (getAspectRatioFrameLayout() != null) { if (getAspectRatioFrameLayout() != null) {
final int currentResizeMode = getAspectRatioFrameLayout().getResizeMode(); final int currentResizeMode = getAspectRatioFrameLayout().getResizeMode();
final int newResizeMode = nextResizeMode(currentResizeMode); final int newResizeMode = nextResizeMode(currentResizeMode);
getAspectRatioFrameLayout().setResizeMode(newResizeMode); setResizeMode(newResizeMode);
getResizeView().setText(PlayerHelper.resizeTypeOf(context, newResizeMode));
} }
} }
protected void setResizeMode(@AspectRatioFrameLayout.ResizeMode final int resizeMode) {
getAspectRatioFrameLayout().setResizeMode(resizeMode);
getResizeView().setText(PlayerHelper.resizeTypeOf(context, resizeMode));
}
protected abstract int nextResizeMode(@AspectRatioFrameLayout.ResizeMode final int resizeMode); protected abstract int nextResizeMode(@AspectRatioFrameLayout.ResizeMode final int resizeMode);
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// SeekBar Listener // SeekBar Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View file

@ -116,7 +116,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener,
private void onAudioFocusGain() { private void onAudioFocusGain() {
Log.d(TAG, "onAudioFocusGain() called"); Log.d(TAG, "onAudioFocusGain() called");
player.setVolume(DUCK_AUDIO_TO); player.setVolume(DUCK_AUDIO_TO);
animateAudio(DUCK_AUDIO_TO, 1f, DUCK_DURATION); animateAudio(DUCK_AUDIO_TO, 1f);
if (PlayerHelper.isResumeAfterAudioFocusGain(context)) { if (PlayerHelper.isResumeAfterAudioFocusGain(context)) {
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
@ -131,13 +131,13 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener,
private void onAudioFocusLossCanDuck() { private void onAudioFocusLossCanDuck() {
Log.d(TAG, "onAudioFocusLossCanDuck() called"); Log.d(TAG, "onAudioFocusLossCanDuck() called");
// Set the volume to 1/10 on ducking // Set the volume to 1/10 on ducking
animateAudio(player.getVolume(), DUCK_AUDIO_TO, DUCK_DURATION); animateAudio(player.getVolume(), DUCK_AUDIO_TO);
} }
private void animateAudio(final float from, final float to, int duration) { private void animateAudio(final float from, final float to) {
ValueAnimator valueAnimator = new ValueAnimator(); ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setFloatValues(from, to); valueAnimator.setFloatValues(from, to);
valueAnimator.setDuration(duration); valueAnimator.setDuration(AudioReactor.DUCK_DURATION);
valueAnimator.addListener(new AnimatorListenerAdapter() { valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {

View file

@ -4,12 +4,9 @@ import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.FileDataSource; import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.upstream.cache.CacheDataSink; import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
@ -17,8 +14,6 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import org.schabi.newpipe.Downloader;
import java.io.File; import java.io.File;
/* package-private */ class CacheFactory implements DataSource.Factory { /* package-private */ class CacheFactory implements DataSource.Factory {

View file

@ -66,25 +66,15 @@ public class PlaybackParameterDialog extends DialogFragment {
private double stepSize = DEFAULT_STEP; private double stepSize = DEFAULT_STEP;
@Nullable private SeekBar tempoSlider; @Nullable private SeekBar tempoSlider;
@Nullable private TextView tempoMinimumText;
@Nullable private TextView tempoMaximumText;
@Nullable private TextView tempoCurrentText; @Nullable private TextView tempoCurrentText;
@Nullable private TextView tempoStepDownText; @Nullable private TextView tempoStepDownText;
@Nullable private TextView tempoStepUpText; @Nullable private TextView tempoStepUpText;
@Nullable private SeekBar pitchSlider; @Nullable private SeekBar pitchSlider;
@Nullable private TextView pitchMinimumText;
@Nullable private TextView pitchMaximumText;
@Nullable private TextView pitchCurrentText; @Nullable private TextView pitchCurrentText;
@Nullable private TextView pitchStepDownText; @Nullable private TextView pitchStepDownText;
@Nullable private TextView pitchStepUpText; @Nullable private TextView pitchStepUpText;
@Nullable private TextView stepSizeOnePercentText;
@Nullable private TextView stepSizeFivePercentText;
@Nullable private TextView stepSizeTenPercentText;
@Nullable private TextView stepSizeTwentyFivePercentText;
@Nullable private TextView stepSizeOneHundredPercentText;
@Nullable private CheckBox unhookingCheckbox; @Nullable private CheckBox unhookingCheckbox;
@Nullable private CheckBox skipSilenceCheckbox; @Nullable private CheckBox skipSilenceCheckbox;
@ -181,8 +171,8 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupTempoControl(@NonNull View rootView) { private void setupTempoControl(@NonNull View rootView) {
tempoSlider = rootView.findViewById(R.id.tempoSeekbar); tempoSlider = rootView.findViewById(R.id.tempoSeekbar);
tempoMinimumText = rootView.findViewById(R.id.tempoMinimumText); TextView tempoMinimumText = rootView.findViewById(R.id.tempoMinimumText);
tempoMaximumText = rootView.findViewById(R.id.tempoMaximumText); TextView tempoMaximumText = rootView.findViewById(R.id.tempoMaximumText);
tempoCurrentText = rootView.findViewById(R.id.tempoCurrentText); tempoCurrentText = rootView.findViewById(R.id.tempoCurrentText);
tempoStepUpText = rootView.findViewById(R.id.tempoStepUp); tempoStepUpText = rootView.findViewById(R.id.tempoStepUp);
tempoStepDownText = rootView.findViewById(R.id.tempoStepDown); tempoStepDownText = rootView.findViewById(R.id.tempoStepDown);
@ -203,8 +193,8 @@ public class PlaybackParameterDialog extends DialogFragment {
private void setupPitchControl(@NonNull View rootView) { private void setupPitchControl(@NonNull View rootView) {
pitchSlider = rootView.findViewById(R.id.pitchSeekbar); pitchSlider = rootView.findViewById(R.id.pitchSeekbar);
pitchMinimumText = rootView.findViewById(R.id.pitchMinimumText); TextView pitchMinimumText = rootView.findViewById(R.id.pitchMinimumText);
pitchMaximumText = rootView.findViewById(R.id.pitchMaximumText); TextView pitchMaximumText = rootView.findViewById(R.id.pitchMaximumText);
pitchCurrentText = rootView.findViewById(R.id.pitchCurrentText); pitchCurrentText = rootView.findViewById(R.id.pitchCurrentText);
pitchStepDownText = rootView.findViewById(R.id.pitchStepDown); pitchStepDownText = rootView.findViewById(R.id.pitchStepDown);
pitchStepUpText = rootView.findViewById(R.id.pitchStepUp); pitchStepUpText = rootView.findViewById(R.id.pitchStepUp);
@ -247,11 +237,11 @@ public class PlaybackParameterDialog extends DialogFragment {
} }
private void setupStepSizeSelector(@NonNull final View rootView) { private void setupStepSizeSelector(@NonNull final View rootView) {
stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent); TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent); TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
stepSizeTenPercentText = rootView.findViewById(R.id.stepSizeTenPercent); TextView stepSizeTenPercentText = rootView.findViewById(R.id.stepSizeTenPercent);
stepSizeTwentyFivePercentText = rootView.findViewById(R.id.stepSizeTwentyFivePercent); TextView stepSizeTwentyFivePercentText = rootView.findViewById(R.id.stepSizeTwentyFivePercent);
stepSizeOneHundredPercentText = rootView.findViewById(R.id.stepSizeOneHundredPercent); TextView stepSizeOneHundredPercentText = rootView.findViewById(R.id.stepSizeOneHundredPercent);
if (stepSizeOnePercentText != null) { if (stepSizeOnePercentText != null) {
stepSizeOnePercentText.setText(getPercentString(STEP_ONE_PERCENT_VALUE)); stepSizeOnePercentText.setText(getPercentString(STEP_ONE_PERCENT_VALUE));

View file

@ -169,8 +169,12 @@ public class PlayerHelper {
return isResumeAfterAudioFocusGain(context, false); return isResumeAfterAudioFocusGain(context, false);
} }
public static boolean isPlayerGestureEnabled(@NonNull final Context context) { public static boolean isVolumeGestureEnabled(@NonNull final Context context) {
return isPlayerGestureEnabled(context, true); return isVolumeGestureEnabled(context, true);
}
public static boolean isBrightnessGestureEnabled(@NonNull final Context context) {
return isBrightnessGestureEnabled(context, true);
} }
public static boolean isUsingOldPlayer(@NonNull final Context context) { public static boolean isUsingOldPlayer(@NonNull final Context context) {
@ -203,7 +207,7 @@ public class PlayerHelper {
@NonNull @NonNull
public static SeekParameters getSeekParameters(@NonNull final Context context) { public static SeekParameters getSeekParameters(@NonNull final Context context) {
return isUsingInexactSeek(context, false) ? return isUsingInexactSeek(context) ?
SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT; SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT;
} }
@ -306,8 +310,12 @@ public class PlayerHelper {
return getPreferences(context).getBoolean(context.getString(R.string.resume_on_audio_focus_gain_key), b); return getPreferences(context).getBoolean(context.getString(R.string.resume_on_audio_focus_gain_key), b);
} }
private static boolean isPlayerGestureEnabled(@NonNull final Context context, final boolean b) { private static boolean isVolumeGestureEnabled(@NonNull final Context context, final boolean b) {
return getPreferences(context).getBoolean(context.getString(R.string.player_gesture_controls_key), b); return getPreferences(context).getBoolean(context.getString(R.string.volume_gesture_control_key), b);
}
private static boolean isBrightnessGestureEnabled(@NonNull final Context context, final boolean b) {
return getPreferences(context).getBoolean(context.getString(R.string.brightness_gesture_control_key), b);
} }
private static boolean isUsingOldPlayer(@NonNull final Context context, final boolean b) { private static boolean isUsingOldPlayer(@NonNull final Context context, final boolean b) {
@ -318,8 +326,8 @@ public class PlayerHelper {
return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b); return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
} }
private static boolean isUsingInexactSeek(@NonNull final Context context, final boolean b) { private static boolean isUsingInexactSeek(@NonNull final Context context) {
return getPreferences(context).getBoolean(context.getString(R.string.use_inexact_seek_key), b); return getPreferences(context).getBoolean(context.getString(R.string.use_inexact_seek_key), false);
} }
private static boolean isAutoQueueEnabled(@NonNull final Context context, final boolean b) { private static boolean isAutoQueueEnabled(@NonNull final Context context, final boolean b) {

View file

@ -79,7 +79,7 @@ public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator
private void publishFloatingQueueWindow() { private void publishFloatingQueueWindow() {
if (callback.getQueueSize() == 0) { if (callback.getQueueSize() == 0) {
mediaSession.setQueue(Collections.<MediaSessionCompat.QueueItem>emptyList()); mediaSession.setQueue(Collections.emptyList());
activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID;
return; return;
} }

View file

@ -8,7 +8,7 @@ import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
public class BasePlayerMediaSession implements MediaSessionCallback { public class BasePlayerMediaSession implements MediaSessionCallback {
private BasePlayer player; private final BasePlayer player;
public BasePlayerMediaSession(final BasePlayer player) { public BasePlayerMediaSession(final BasePlayer player) {
this.player = player; this.player = player;

View file

@ -4,7 +4,6 @@ import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
@ -12,7 +11,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/** /**
* This class allows irregular text language labels for use when selecting text captions and * This class allows irregular text language labels for use when selecting text captions and
@ -55,7 +53,7 @@ public class CustomTrackSelector extends DefaultTrackSelector {
/** @see DefaultTrackSelector#selectTextTrack(TrackGroupArray, int[][], Parameters) */ /** @see DefaultTrackSelector#selectTextTrack(TrackGroupArray, int[][], Parameters) */
@Override @Override
protected TrackSelection selectTextTrack(TrackGroupArray groups, int[][] formatSupport, protected TrackSelection selectTextTrack(TrackGroupArray groups, int[][] formatSupport,
Parameters params) throws ExoPlaybackException { Parameters params) {
TrackGroup selectedGroup = null; TrackGroup selectedGroup = null;
int selectedTrackIndex = 0; int selectedTrackIndex = 0;
int selectedTrackScore = 0; int selectedTrackScore = 0;

View file

@ -335,7 +335,7 @@ public class MediaSourceManager {
private void loadImmediate() { private void loadImmediate() {
if (DEBUG) Log.d(TAG, "MediaSource - loadImmediate() called"); if (DEBUG) Log.d(TAG, "MediaSource - loadImmediate() called");
final ItemsToLoad itemsToLoad = getItemsToLoad(playQueue, WINDOW_SIZE); final ItemsToLoad itemsToLoad = getItemsToLoad(playQueue);
if (itemsToLoad == null) return; if (itemsToLoad == null) return;
// Evict the previous items being loaded to free up memory, before start loading new ones // Evict the previous items being loaded to free up memory, before start loading new ones
@ -472,8 +472,7 @@ public class MediaSourceManager {
// Manager Helpers // Manager Helpers
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Nullable @Nullable
private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue, private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) {
final int windowSize) {
// The current item has higher priority // The current item has higher priority
final int currentIndex = playQueue.getIndex(); final int currentIndex = playQueue.getIndex();
final PlayQueueItem currentItem = playQueue.getItem(currentIndex); final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
@ -482,8 +481,8 @@ public class MediaSourceManager {
// The rest are just for seamless playback // The rest are just for seamless playback
// Although timeline is not updated prior to the current index, these sources are still // Although timeline is not updated prior to the current index, these sources are still
// loaded into the cache for faster retrieval at a potentially later time. // loaded into the cache for faster retrieval at a potentially later time.
final int leftBound = Math.max(0, currentIndex - windowSize); final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE);
final int rightLimit = currentIndex + windowSize + 1; final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1;
final int rightBound = Math.min(playQueue.size(), rightLimit); final int rightBound = Math.min(playQueue.size(), rightLimit);
final Set<PlayQueueItem> neighbors = new ArraySet<>( final Set<PlayQueueItem> neighbors = new ArraySet<>(
playQueue.getStreams().subList(leftBound,rightBound)); playQueue.getStreams().subList(leftBound,rightBound));

View file

@ -8,8 +8,6 @@ import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.util.List;
public interface PlaybackListener { public interface PlaybackListener {
/** /**

View file

@ -19,14 +19,14 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
boolean isInitial; boolean isInitial;
boolean isComplete; boolean isComplete;
int serviceId; final int serviceId;
String baseUrl; final String baseUrl;
String nextUrl; String nextUrl;
transient Disposable fetchReactor; transient Disposable fetchReactor;
AbstractInfoPlayQueue(final U item) { AbstractInfoPlayQueue(final U item) {
this(item.getServiceId(), item.getUrl(), null, Collections.<StreamInfoItem>emptyList(), 0); this(item.getServiceId(), item.getUrl(), null, Collections.emptyList(), 0);
} }
AbstractInfoPlayQueue(final int serviceId, AbstractInfoPlayQueue(final int serviceId,

View file

@ -5,10 +5,8 @@ import android.text.TextUtils;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;

View file

@ -6,8 +6,6 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
/** /**
* Created by Christian Schabesberger on 01.08.16. * Created by Christian Schabesberger on 01.08.16.

View file

@ -5,7 +5,6 @@ import android.support.annotation.NonNull;
import org.acra.collector.CrashReportData; import org.acra.collector.CrashReportData;
import org.acra.sender.ReportSender; import org.acra.sender.ReportSender;
import org.acra.sender.ReportSenderException;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
/* /*
@ -31,7 +30,7 @@ import org.schabi.newpipe.R;
public class AcraReportSender implements ReportSender { public class AcraReportSender implements ReportSender {
@Override @Override
public void send(@NonNull Context context, @NonNull CrashReportData report) throws ReportSenderException { public void send(@NonNull Context context, @NonNull CrashReportData report) {
ErrorActivity.reportError(context, report, ErrorActivity.reportError(context, report,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,"none", ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,"none",
"App crash, UI failure", R.string.app_ui_crash)); "App crash, UI failure", R.string.app_ui_crash));

View file

@ -3,7 +3,6 @@ package org.schabi.newpipe.report;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
@ -46,7 +45,6 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.Vector; import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
/* /*
* Created by Christian Schabesberger on 24.10.15. * Created by Christian Schabesberger on 24.10.15.
@ -81,12 +79,7 @@ public class ErrorActivity extends AppCompatActivity {
private ErrorInfo errorInfo; private ErrorInfo errorInfo;
private Class returnActivity; private Class returnActivity;
private String currentTimeStamp; private String currentTimeStamp;
// views
private TextView errorView;
private EditText userCommentBox; private EditText userCommentBox;
private Button reportButton;
private TextView infoView;
private TextView errorMessageView;
public static void reportUiError(final AppCompatActivity activity, final Throwable el) { public static void reportUiError(final AppCompatActivity activity, final Throwable el) {
reportError(activity, el, activity.getClass(), null, reportError(activity, el, activity.getClass(), null,
@ -194,11 +187,11 @@ public class ErrorActivity extends AppCompatActivity {
actionBar.setDisplayShowTitleEnabled(true); actionBar.setDisplayShowTitleEnabled(true);
} }
reportButton = findViewById(R.id.errorReportButton); Button reportButton = findViewById(R.id.errorReportButton);
userCommentBox = findViewById(R.id.errorCommentBox); userCommentBox = findViewById(R.id.errorCommentBox);
errorView = findViewById(R.id.errorView); TextView errorView = findViewById(R.id.errorView);
infoView = findViewById(R.id.errorInfosView); TextView infoView = findViewById(R.id.errorInfosView);
errorMessageView = findViewById(R.id.errorMessageView); TextView errorMessageView = findViewById(R.id.errorMessageView);
ActivityCommunicator ac = ActivityCommunicator.getCommunicator(); ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
returnActivity = ac.returnActivity; returnActivity = ac.returnActivity;
@ -281,15 +274,14 @@ public class ErrorActivity extends AppCompatActivity {
} }
private String formErrorText(String[] el) { private String formErrorText(String[] el) {
String text = ""; StringBuilder text = new StringBuilder();
if (el != null) { if (el != null) {
for (String e : el) { for (String e : el) {
text += "-------------------------------------\n" text.append("-------------------------------------\n").append(e);
+ e;
} }
} }
text += "-------------------------------------"; text.append("-------------------------------------");
return text; return text.toString();
} }
/** /**

View file

@ -6,7 +6,6 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.Log;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
@ -49,7 +48,7 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
return super.onPreferenceTreeClick(preference); return super.onPreferenceTreeClick(preference);
} }
private Preference.OnPreferenceChangeListener themePreferenceChange = new Preference.OnPreferenceChangeListener() { private final Preference.OnPreferenceChangeListener themePreferenceChange = new Preference.OnPreferenceChangeListener() {
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply(); defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply();

View file

@ -13,7 +13,7 @@ import org.schabi.newpipe.MainActivity;
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected boolean DEBUG = MainActivity.DEBUG; protected final boolean DEBUG = MainActivity.DEBUG;
protected SharedPreferences defaultPreferences; protected SharedPreferences defaultPreferences;

View file

@ -9,8 +9,6 @@ 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.v4.app.Fragment;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
@ -20,11 +18,10 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.utils.Localization;
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.FilePickerActivityHelper; import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ZipHelper; import org.schabi.newpipe.util.ZipHelper;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -47,7 +44,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945; private static final int REQUEST_IMPORT_PATH = 8945;
private static final int REQUEST_EXPORT_PATH = 30945; private static final int REQUEST_EXPORT_PATH = 30945;
private String homeDir;
private File databasesDir; private File databasesDir;
private File newpipe_db; private File newpipe_db;
private File newpipe_db_journal; private File newpipe_db_journal;
@ -81,7 +77,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
homeDir = getActivity().getApplicationInfo().dataDir; String homeDir = getActivity().getApplicationInfo().dataDir;
databasesDir = new File(homeDir + "/databases"); databasesDir = new File(homeDir + "/databases");
newpipe_db = new File(homeDir + "/databases/newpipe.db"); newpipe_db = new File(homeDir + "/databases/newpipe.db");
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal"); newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
@ -112,6 +108,20 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
startActivityForResult(i, REQUEST_EXPORT_PATH); startActivityForResult(i, REQUEST_EXPORT_PATH);
return true; return true;
}); });
Preference setPreferredLanguage = findPreference(getString(R.string.content_language_key));
setPreferredLanguage.setOnPreferenceChangeListener((Preference p, Object newLanguage) -> {
Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity());
NewPipe.setLocalization(new Localization(oldLocal.getCountry(), (String) newLanguage));
return true;
});
Preference setPreferredCountry = findPreference(getString(R.string.content_country_key));
setPreferredCountry.setOnPreferenceChangeListener((Preference p, Object newCountry) -> {
Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity());
NewPipe.setLocalization(new Localization((String) newCountry, oldLocal.getLanguage()));
return true;
});
} }
@Override @Override
@ -193,7 +203,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
} finally { } finally {
try { try {
zipFile.close(); zipFile.close();
} catch (Exception e){} } catch (Exception ignored){}
} }
try { try {
@ -254,17 +264,17 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
String key = entry.getKey(); String key = entry.getKey();
if (v instanceof Boolean) if (v instanceof Boolean)
prefEdit.putBoolean(key, ((Boolean) v).booleanValue()); prefEdit.putBoolean(key, (Boolean) v);
else if (v instanceof Float) else if (v instanceof Float)
prefEdit.putFloat(key, ((Float) v).floatValue()); prefEdit.putFloat(key, (Float) v);
else if (v instanceof Integer) else if (v instanceof Integer)
prefEdit.putInt(key, ((Integer) v).intValue()); prefEdit.putInt(key, (Integer) v);
else if (v instanceof Long) else if (v instanceof Long)
prefEdit.putLong(key, ((Long) v).longValue()); prefEdit.putLong(key, (Long) v);
else if (v instanceof String) else if (v instanceof String)
prefEdit.putString(key, ((String) v)); prefEdit.putString(key, ((String) v));
} }
prefEdit.commit(); prefEdit.apply();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
@ -286,13 +296,12 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
// Error // Error
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected boolean onError(Throwable e) { protected void onError(Throwable e) {
final Activity activity = getActivity(); final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, ErrorActivity.reportError(activity, e,
activity.getClass(), activity.getClass(),
null, null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash)); "none", "", R.string.app_ui_crash));
return true;
} }
} }

View file

@ -1,29 +1,20 @@
package org.schabi.newpipe.settings; package org.schabi.newpipe.settings;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.local.history.HistoryRecordManager;
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.InfoCache; import org.schabi.newpipe.util.InfoCache;
import java.util.ArrayList;
import java.util.Collection;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.Disposables;
public class HistorySettingsFragment extends BasePreferenceFragment { public class HistorySettingsFragment extends BasePreferenceFragment {
private String cacheWipeKey; private String cacheWipeKey;

View file

@ -51,9 +51,7 @@ import io.reactivex.schedulers.Schedulers;
*/ */
public class SelectChannelFragment extends DialogFragment { public class SelectChannelFragment extends DialogFragment {
private SelectChannelAdapter channelAdapter; private final ImageLoader imageLoader = ImageLoader.getInstance();
private SubscriptionService subscriptionService;
private ImageLoader imageLoader = ImageLoader.getInstance();
private ProgressBar progressBar; private ProgressBar progressBar;
private TextView emptyView; private TextView emptyView;
@ -89,9 +87,9 @@ public class SelectChannelFragment extends DialogFragment {
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.select_channel_fragment, container, false); View v = inflater.inflate(R.layout.select_channel_fragment, container, false);
recyclerView = (RecyclerView) v.findViewById(R.id.items_list); recyclerView = v.findViewById(R.id.items_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
channelAdapter = new SelectChannelAdapter(); SelectChannelAdapter channelAdapter = new SelectChannelAdapter();
recyclerView.setAdapter(channelAdapter); recyclerView.setAdapter(channelAdapter);
progressBar = v.findViewById(R.id.progressBar); progressBar = v.findViewById(R.id.progressBar);
@ -101,7 +99,7 @@ public class SelectChannelFragment extends DialogFragment {
emptyView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE);
subscriptionService = SubscriptionService.getInstance(getContext()); SubscriptionService subscriptionService = SubscriptionService.getInstance(getContext());
subscriptionService.getSubscription().toObservable() subscriptionService.getSubscription().toObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -203,9 +201,9 @@ public class SelectChannelFragment extends DialogFragment {
thumbnailView = v.findViewById(R.id.itemThumbnailView); thumbnailView = v.findViewById(R.id.itemThumbnailView);
titleView = v.findViewById(R.id.itemTitleView); titleView = v.findViewById(R.id.itemTitleView);
} }
public View view; public final View view;
public CircleImageView thumbnailView; public final CircleImageView thumbnailView;
public TextView titleView; public final TextView titleView;
} }
} }
@ -213,14 +211,13 @@ public class SelectChannelFragment extends DialogFragment {
// Error // Error
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected boolean onError(Throwable e) { protected void onError(Throwable e) {
final Activity activity = getActivity(); final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, ErrorActivity.reportError(activity, e,
activity.getClass(), activity.getClass(),
null, null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash)); "none", "", R.string.app_ui_crash));
return true;
} }

View file

@ -75,7 +75,7 @@ public class SelectKioskFragment extends DialogFragment {
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false); View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false);
recyclerView = (RecyclerView) v.findViewById(R.id.items_list); recyclerView = v.findViewById(R.id.items_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
try { try {
selectKioskAdapter = new SelectKioskAdapter(); selectKioskAdapter = new SelectKioskAdapter();
@ -112,13 +112,13 @@ public class SelectKioskFragment extends DialogFragment {
public Entry (int i, int si, String ki, String kn){ public Entry (int i, int si, String ki, String kn){
icon = i; serviceId=si; kioskId=ki; kioskName = kn; icon = i; serviceId=si; kioskId=ki; kioskName = kn;
} }
int icon; final int icon;
int serviceId; final int serviceId;
String kioskId; final String kioskId;
String kioskName; final String kioskName;
} }
private List<Entry> kioskList = new Vector<>(); private final List<Entry> kioskList = new Vector<>();
public SelectKioskAdapter() public SelectKioskAdapter()
throws Exception { throws Exception {
@ -157,9 +157,9 @@ public class SelectKioskFragment extends DialogFragment {
thumbnailView = v.findViewById(R.id.itemThumbnailView); thumbnailView = v.findViewById(R.id.itemThumbnailView);
titleView = v.findViewById(R.id.itemTitleView); titleView = v.findViewById(R.id.itemTitleView);
} }
public View view; public final View view;
public ImageView thumbnailView; public final ImageView thumbnailView;
public TextView titleView; public final TextView titleView;
} }
public void onBindViewHolder(SelectKioskItemHolder holder, final int position) { public void onBindViewHolder(SelectKioskItemHolder holder, final int position) {
@ -179,13 +179,12 @@ public class SelectKioskFragment extends DialogFragment {
// Error // Error
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected boolean onError(Throwable e) { protected void onError(Throwable e) {
final Activity activity = getActivity(); final Activity activity = getActivity();
ErrorActivity.reportError(activity, e, ErrorActivity.reportError(activity, e,
activity.getClass(), activity.getClass(),
null, null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash)); "none", "", R.string.app_ui_crash));
return true;
} }
} }

View file

@ -32,6 +32,7 @@ import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
@ -67,42 +68,37 @@ public final class ExtractorHelper {
public static Single<SearchInfo> searchFor(final int serviceId, public static Single<SearchInfo> searchFor(final int serviceId,
final String searchString, final String searchString,
final List<String> contentFilter, final List<String> contentFilter,
final String sortFilter, final String sortFilter) {
final String contentCountry) {
checkServiceId(serviceId); checkServiceId(serviceId);
return Single.fromCallable(() -> return Single.fromCallable(() ->
SearchInfo.getInfo(NewPipe.getService(serviceId), SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId) NewPipe.getService(serviceId)
.getSearchQHFactory() .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter), .fromQuery(searchString, contentFilter, sortFilter)));
contentCountry));
} }
public static Single<InfoItemsPage> getMoreSearchItems(final int serviceId, public static Single<InfoItemsPage> getMoreSearchItems(final int serviceId,
final String searchString, final String searchString,
final List<String> contentFilter, final List<String> contentFilter,
final String sortFilter, final String sortFilter,
final String pageUrl, final String pageUrl) {
final String contentCountry) {
checkServiceId(serviceId); checkServiceId(serviceId);
return Single.fromCallable(() -> return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId), SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId) NewPipe.getService(serviceId)
.getSearchQHFactory() .getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter), .fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl)); pageUrl));
} }
public static Single<List<String>> suggestionsFor(final int serviceId, public static Single<List<String>> suggestionsFor(final int serviceId,
final String query, final String query) {
final String contentCountry) {
checkServiceId(serviceId); checkServiceId(serviceId);
return Single.fromCallable(() -> return Single.fromCallable(() ->
NewPipe.getService(serviceId) NewPipe.getService(serviceId)
.getSuggestionExtractor() .getSuggestionExtractor()
.suggestionList(query, contentCountry)); .suggestionList(query));
} }
public static Single<StreamInfo> getStreamInfo(final int serviceId, public static Single<StreamInfo> getStreamInfo(final int serviceId,
@ -147,19 +143,17 @@ public final class ExtractorHelper {
public static Single<KioskInfo> getKioskInfo(final int serviceId, public static Single<KioskInfo> getKioskInfo(final int serviceId,
final String url, final String url,
final String contentCountry,
boolean forceLoad) { boolean forceLoad) {
return checkCache(forceLoad, serviceId, url, Single.fromCallable(() -> return checkCache(forceLoad, serviceId, url, Single.fromCallable(() ->
KioskInfo.getInfo(NewPipe.getService(serviceId), url, contentCountry))); KioskInfo.getInfo(NewPipe.getService(serviceId), url)));
} }
public static Single<InfoItemsPage> getMoreKioskItems(final int serviceId, public static Single<InfoItemsPage> getMoreKioskItems(final int serviceId,
final String url, final String url,
final String nextStreamsUrl, final String nextStreamsUrl) {
final String contentCountry) {
return Single.fromCallable(() -> return Single.fromCallable(() ->
KioskInfo.getMoreItems(NewPipe.getService(serviceId), KioskInfo.getMoreItems(NewPipe.getService(serviceId),
url, nextStreamsUrl, contentCountry)); url, nextStreamsUrl));
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -183,7 +177,7 @@ public final class ExtractorHelper {
cache.removeInfo(serviceId, url); cache.removeInfo(serviceId, url);
load = loadFromNetwork; load = loadFromNetwork;
} else { } else {
load = Maybe.concat(ExtractorHelper.<I>loadFromCache(serviceId, url), load = Maybe.concat(ExtractorHelper.loadFromCache(serviceId, url),
loadFromNetwork.toMaybe()) loadFromNetwork.toMaybe())
.firstElement() //Take the first valid .firstElement() //Take the first valid
.toSingle(); .toSingle();

View file

@ -28,9 +28,6 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.Info;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
public final class InfoCache { public final class InfoCache {
@ -58,7 +55,7 @@ public final class InfoCache {
public Info getFromKey(int serviceId, @NonNull String url) { public Info getFromKey(int serviceId, @NonNull String url) {
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]"); if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
synchronized (lruCache) { synchronized (lruCache) {
return getInfo(lruCache, keyOf(serviceId, url)); return getInfo(keyOf(serviceId, url));
} }
} }
@ -89,7 +86,7 @@ public final class InfoCache {
public void trimCache() { public void trimCache() {
if (DEBUG) Log.d(TAG, "trimCache() called"); if (DEBUG) Log.d(TAG, "trimCache() called");
synchronized (lruCache) { synchronized (lruCache) {
removeStaleCache(lruCache); removeStaleCache();
lruCache.trimToSize(TRIM_CACHE_TO); lruCache.trimToSize(TRIM_CACHE_TO);
} }
} }
@ -105,23 +102,22 @@ public final class InfoCache {
return serviceId + url; return serviceId + url;
} }
private static void removeStaleCache(@NonNull final LruCache<String, CacheData> cache) { private static void removeStaleCache() {
for (Map.Entry<String, CacheData> entry : cache.snapshot().entrySet()) { for (Map.Entry<String, CacheData> entry : InfoCache.lruCache.snapshot().entrySet()) {
final CacheData data = entry.getValue(); final CacheData data = entry.getValue();
if (data != null && data.isExpired()) { if (data != null && data.isExpired()) {
cache.remove(entry.getKey()); InfoCache.lruCache.remove(entry.getKey());
} }
} }
} }
@Nullable @Nullable
private static Info getInfo(@NonNull final LruCache<String, CacheData> cache, private static Info getInfo(@NonNull final String key) {
@NonNull final String key) { final CacheData data = InfoCache.lruCache.get(key);
final CacheData data = cache.get(key);
if (data == null) return null; if (data == null) return null;
if (data.isExpired()) { if (data.isExpired()) {
cache.remove(key); InfoCache.lruCache.remove(key);
return null; return null;
} }

View file

@ -204,7 +204,7 @@ public final class ListHelper {
*/ */
private static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) { private static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
Collections.sort(videoStreams, (o1, o2) -> { Collections.sort(videoStreams, (o1, o2) -> {
int result = compareVideoStreamResolution(o1, o2, VIDEO_FORMAT_QUALITY_RANKING); int result = compareVideoStreamResolution(o1, o2);
return result == 0 ? 0 : (ascendingOrder ? result : -result); return result == 0 ? 0 : (ascendingOrder ? result : -result);
}); });
} }
@ -399,8 +399,7 @@ public final class ListHelper {
} }
// Compares the quality of two video streams. // Compares the quality of two video streams.
private static int compareVideoStreamResolution(VideoStream streamA, VideoStream streamB, private static int compareVideoStreamResolution(VideoStream streamA, VideoStream streamB) {
List<MediaFormat> formatRanking) {
if (streamA == null) { if (streamA == null) {
return -1; return -1;
} }
@ -414,7 +413,7 @@ public final class ListHelper {
} }
// Same bitrate and format // Same bitrate and format
return formatRanking.indexOf(streamA.getFormat()) - formatRanking.indexOf(streamB.getFormat()); return ListHelper.VIDEO_FORMAT_QUALITY_RANKING.indexOf(streamA.getFormat()) - ListHelper.VIDEO_FORMAT_QUALITY_RANKING.indexOf(streamB.getFormat());
} }

View file

@ -69,10 +69,23 @@ public class Localization {
return stringBuilder.toString(); return stringBuilder.toString();
} }
public static org.schabi.newpipe.extractor.utils.Localization getPreferredExtractorLocal(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String languageCode = sp.getString(context.getString(R.string.content_language_key),
context.getString(R.string.default_language_value));
String countryCode = sp.getString(context.getString(R.string.content_country_key),
context.getString(R.string.default_country_value));
return new org.schabi.newpipe.extractor.utils.Localization(countryCode, languageCode);
}
public static Locale getPreferredLocale(Context context) { public static Locale getPreferredLocale(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String languageCode = sp.getString(context.getString(R.string.search_language_key), context.getString(R.string.default_language_value)); String languageCode = sp.getString(context.getString(R.string.content_language_key),
context.getString(R.string.default_language_value));
try { try {
if (languageCode.length() == 2) { if (languageCode.length() == 2) {

View file

@ -26,7 +26,6 @@ import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;

View file

@ -21,7 +21,6 @@ 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;

View file

@ -31,7 +31,7 @@ import us.shandian.giga.util.Utility;
public class StreamItemAdapter<T extends Stream> extends BaseAdapter { public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
private final Context context; private final Context context;
private StreamSizeWrapper<T> streamsWrapper; private final StreamSizeWrapper<T> streamsWrapper;
private final boolean showIconNoAudio; private final boolean showIconNoAudio;
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, boolean showIconNoAudio) { public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, boolean showIconNoAudio) {
@ -124,7 +124,7 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
public static class StreamSizeWrapper<T extends Stream> implements Serializable { public static class StreamSizeWrapper<T extends Stream> implements Serializable {
private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(Collections.emptyList()); private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(Collections.emptyList());
private final List<T> streamsList; private final List<T> streamsList;
private long[] streamSizes; private final long[] streamSizes;
public StreamSizeWrapper(List<T> streamsList) { public StreamSizeWrapper(List<T> streamsList) {
this.streamsList = streamsList; this.streamsList = streamsList;

View file

@ -56,7 +56,6 @@ public class ZipHelper {
/** /**
* This will extract data from Zipfiles. * This will extract data from Zipfiles.
* Caution this will override the original file. * Caution this will override the original file.
* @param inZip The ZipOutputStream where the data is stored in
* @param file The path of the file on the disk where the data should be extracted to. * @param file The path of the file on the disk where the data should be extracted to.
* @param name The path of the file inside the zip. * @param name The path of the file inside the zip.
* @return will return true if the file was found within the zip file * @return will return true if the file was found within the zip file

View file

@ -81,7 +81,7 @@ public class CollapsibleView extends LinearLayout {
private int targetHeight = -1; private int targetHeight = -1;
private ValueAnimator currentAnimator; private ValueAnimator currentAnimator;
private List<StateListener> listeners = new ArrayList<>(); private final List<StateListener> listeners = new ArrayList<>();
/** /**
* This method recalculates the height of this view so it <b>must</b> be called when * This method recalculates the height of this view so it <b>must</b> be called when

View file

@ -123,7 +123,7 @@ public class DownloadManagerImpl implements DownloadManager {
Collections.sort(missions, new Comparator<DownloadMission>() { Collections.sort(missions, new Comparator<DownloadMission>() {
@Override @Override
public int compare(DownloadMission o1, DownloadMission o2) { public int compare(DownloadMission o1, DownloadMission o2) {
return Long.valueOf(o1.timestamp).compareTo(o2.timestamp); return Long.compare(o1.timestamp, o2.timestamp);
} }
}); });
} }

View file

@ -67,8 +67,8 @@ public class DownloadMission implements Serializable {
public long done; public long done;
public int threadCount = 3; public int threadCount = 3;
public int finishCount; public int finishCount;
private List<Long> threadPositions = new ArrayList<Long>(); private final List<Long> threadPositions = new ArrayList<>();
public final Map<Long, Boolean> blockState = new HashMap<Long, Boolean>(); public final Map<Long, Boolean> blockState = new HashMap<>();
public boolean running; public boolean running;
public boolean finished; public boolean finished;
public boolean fallback; public boolean fallback;
@ -77,7 +77,7 @@ public class DownloadMission implements Serializable {
public transient boolean recovered; public transient boolean recovered;
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>(); private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<>();
private transient boolean mWritingToFile; private transient boolean mWritingToFile;
private static final int NO_IDENTIFIER = -1; private static final int NO_IDENTIFIER = -1;
@ -232,7 +232,7 @@ public class DownloadMission implements Serializable {
public synchronized void addListener(MissionListener listener) { public synchronized void addListener(MissionListener listener) {
Handler handler = new Handler(Looper.getMainLooper()); Handler handler = new Handler(Looper.getMainLooper());
MissionListener.handlerStore.put(listener, handler); MissionListener.handlerStore.put(listener, handler);
mListeners.add(new WeakReference<MissionListener>(listener)); mListeners.add(new WeakReference<>(listener));
} }
public synchronized void removeListener(MissionListener listener) { public synchronized void removeListener(MissionListener listener) {

View file

@ -92,7 +92,7 @@ public class DownloadRunnable implements Runnable {
// A server may be ignoring the range request // A server may be ignoring the range request
if (conn.getResponseCode() != 206) { if (conn.getResponseCode() != 206) {
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED; mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED); notifyError();
if (DEBUG) { if (DEBUG) {
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode()); Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
@ -161,9 +161,9 @@ public class DownloadRunnable implements Runnable {
} }
} }
private void notifyError(final int err) { private void notifyError() {
synchronized (mMission) { synchronized (mMission) {
mMission.notifyError(err); mMission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
mMission.pause(); mMission.pause();
} }
} }

View file

@ -56,7 +56,7 @@ public class DownloadManagerService extends Service {
private DownloadDataSource mDataSource; private DownloadDataSource mDataSource;
private MissionListener missionListener = new MissionListener(); private final MissionListener missionListener = new MissionListener();
private void notifyMediaScanner(DownloadMission mission) { private void notifyMediaScanner(DownloadMission mission) {

View file

@ -306,12 +306,12 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
public DownloadMission mission; public DownloadMission mission;
public int position; public int position;
public TextView status; public final TextView status;
public ImageView icon; public final ImageView icon;
public TextView name; public final TextView name;
public TextView size; public final TextView size;
public View bkg; public final View bkg;
public ImageView menu; public final ImageView menu;
public ProgressDrawable progress; public ProgressDrawable progress;
public MissionObserver observer; public MissionObserver observer;
@ -332,8 +332,8 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
} }
static class MissionObserver implements DownloadMission.MissionListener { static class MissionObserver implements DownloadMission.MissionListener {
private MissionAdapter mAdapter; private final MissionAdapter mAdapter;
private ViewHolder mHolder; private final ViewHolder mHolder;
public MissionObserver(MissionAdapter adapter, ViewHolder holder) { public MissionObserver(MissionAdapter adapter, ViewHolder holder) {
mAdapter = adapter; mAdapter = adapter;
@ -365,7 +365,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
private static class ChecksumTask extends AsyncTask<String, Void, String> { private static class ChecksumTask extends AsyncTask<String, Void, String> {
ProgressDialog prog; ProgressDialog prog;
WeakReference<Activity> weakReference; final WeakReference<Activity> weakReference;
ChecksumTask(@NonNull Activity activity) { ChecksumTask(@NonNull Activity activity) {
weakReference = new WeakReference<>(activity); weakReference = new WeakReference<>(activity);

View file

@ -12,7 +12,8 @@ import android.support.v4.content.ContextCompat;
public class ProgressDrawable extends Drawable { public class ProgressDrawable extends Drawable {
private float mProgress; private float mProgress;
private int mBackgroundColor, mForegroundColor; private final int mBackgroundColor;
private final int mForegroundColor;
public ProgressDrawable(Context context, @ColorRes int background, @ColorRes int foreground) { public ProgressDrawable(Context context, @ColorRes int background, @ColorRes int foreground) {
this(ContextCompat.getColor(context, background), ContextCompat.getColor(context, foreground)); this(ContextCompat.getColor(context, background), ContextCompat.getColor(context, foreground));

View file

@ -16,6 +16,8 @@ import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -44,7 +46,7 @@ public abstract class MissionsFragment extends Fragment {
private DeleteDownloadManager mDeleteDownloadManager; private DeleteDownloadManager mDeleteDownloadManager;
private Disposable mDeleteDisposable; private Disposable mDeleteDisposable;
private ServiceConnection mConnection = new ServiceConnection() { private final ServiceConnection mConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceConnected(ComponentName name, IBinder binder) {
@ -144,17 +146,21 @@ public abstract class MissionsFragment extends Fragment {
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public void onPrepareOptionsMenu(Menu menu) {
return super.onOptionsItemSelected(item); mSwitch = menu.findItem(R.id.switch_mode);
super.onPrepareOptionsMenu(menu);
}
/*switch (item.getItemId()) { @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.switch_mode: case R.id.switch_mode:
mLinear = !mLinear; mLinear = !mLinear;
updateList(); updateList();
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
}*/ }
} }
public void notifyChange() { public void notifyChange() {

View file

@ -11,9 +11,7 @@ import android.widget.Toast;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -198,7 +196,7 @@ public class Utility {
while ((len = i.read(buf)) != -1) { while ((len = i.read(buf)) != -1) {
md.update(buf, 0, len); md.update(buf, 0, len);
} }
} catch (IOException e) { } catch (IOException ignored) {
} }

View file

@ -0,0 +1,581 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_gravity="center">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<View
android:id="@+id/surfaceForeground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
<com.google.android.exoplayer2.ui.SubtitleView
android:id="@+id/subtitleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_gravity="center"/>
<ImageView
android:id="@+id/endScreen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="gone"
tools:background="@android:color/white"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
<RelativeLayout
android:id="@+id/playQueuePanel"
android:layout_width="380dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:visibility="invisible"
android:background="?attr/queue_background_color"
tools:visibility="visible">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/playQueueControl">
<ImageButton
android:id="@+id/playQueueClose"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="40dp"
android:layout_marginEnd="40dp"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
android:tint="?attr/colorAccent"
android:src="@drawable/ic_close_white_24dp"
android:background="?android:selectableItemBackground"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/repeatButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="40dp"
android:layout_marginStart="40dp"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
android:tint="?attr/colorAccent"
android:src="@drawable/exo_controls_repeat_off"
android:background="?android:selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton
android:id="@+id/shuffleButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/repeatButton"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
android:tint="?attr/colorAccent"
android:src="@drawable/ic_shuffle_white_24dp"
android:background="?android:selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/playQueue"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/playQueueControl"
android:scrollbars="vertical"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/play_queue_item"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/playbackControlRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/video_overlay_color"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:id="@+id/playbackWindowRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<RelativeLayout
android:id="@+id/topControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@drawable/player_top_controls_bg"
android:gravity="top"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
tools:ignore="RtlHardcoded">
<LinearLayout
android:id="@+id/metadataView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/qualityTextView"
android:gravity="top"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="15sp"
android:textStyle="bold"
android:clickable="true"
android:focusable="true"
tools:ignore="RtlHardcoded"
tools:text="The Video Title LONG very LONG"/>
<TextView
android:id="@+id/channelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="12sp"
android:clickable="true"
android:focusable="true"
tools:text="The Video Artist LONG very LONG very Long"/>
</LinearLayout>
<TextView
android:id="@+id/qualityTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/playbackSpeed"
android:gravity="center"
android:minWidth="50dp"
android:text="720p"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="HardcodedText,RtlHardcoded"/>
<TextView
android:id="@+id/playbackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/queueButton"
android:gravity="center"
android:minHeight="35dp"
android:minWidth="40dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="RtlHardcoded,RtlSymmetry"
tools:text="1x" />
<ImageButton
android:id="@+id/queueButton"
android:layout_width="30dp"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/moreOptionsButton"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/list"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton
android:id="@+id/moreOptionsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="2dp"
android:padding="5dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_expand_more_white_24dp"
android:background="?attr/selectableItemBackgroundBorderless"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/secondaryControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/topControls"
android:gravity="top"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:visibility="invisible"
tools:ignore="RtlHardcoded"
tools:visibility="visible">
<TextView
android:id="@+id/resizeTextView"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_alignParentLeft="true"
android:gravity="center"
android:minWidth="50dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="HardcodedText,RtlHardcoded"
tools:text="FIT"/>
<TextView
android:id="@+id/captionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@id/switchBackground"
android:layout_toRightOf="@id/resizeTextView"
android:gravity="center|left"
android:minHeight="35dp"
android:minWidth="40dp"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="@android:color/white"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
tools:ignore="RelativeOverlap,RtlHardcoded"
tools:text="English" />
<ImageButton
android:id="@+id/toggleOrientation"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="2dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_screen_rotation_white"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/toggle_orientation"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/switchPopup"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/toggleOrientation"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
android:src="@drawable/ic_fullscreen_exit_white"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_popup"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/switchBackground"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/switchPopup"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_headset_white_24dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/bottomControls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/player_controls_bg"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/playbackCurrentTime"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:minHeight="40dp"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
tools:text="1:06:29"/>
<android.support.v7.widget.AppCompatSeekBar
android:id="@+id/playbackSeekBar"
style="@style/Widget.AppCompat.SeekBar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingBottom="4dp"
android:paddingTop="8dp"
tools:progress="25"
tools:secondaryProgress="50"/>
<TextView
android:id="@+id/playbackEndTime"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="-:--:--"
android:textColor="@android:color/white"
tools:ignore="HardcodedText"
tools:text="1:23:49"/>
<TextView
android:id="@+id/playbackLiveSync"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:gravity="center"
android:text="@string/duration_live_button"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:maxLength="4"
android:visibility="gone"
android:background="?attr/selectableItemBackground"
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
</LinearLayout>
</RelativeLayout>
<ImageButton
android:id="@+id/playPauseButton"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/playPreviousButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:layout_centerInParent="true"
android:layout_toLeftOf="@id/playPauseButton"
android:layout_toStartOf="@id/playPauseButton"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/exo_controls_previous"
tools:ignore="ContentDescription"/>
<ImageButton
android:id="@+id/playNextButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_centerInParent="true"
android:layout_toRightOf="@id/playPauseButton"
android:layout_toEndOf="@id/playPauseButton"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/exo_controls_next"
tools:ignore="ContentDescription"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/controlAnimationView"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_action_av_fast_rewind"
tools:visibility="visible" />
</LinearLayout>
<RelativeLayout
android:id="@+id/loading_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:visibility="gone">
<ProgressBar
android:id="@+id/progressBarLoadingPanel"
style="?android:attr/progressBarStyleLargeInverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_gravity="center"
android:layout_toEndOf="@+id/loading_panel"
android:layout_toRightOf="@+id/loading_panel"
tools:ignore="RtlHardcoded">
<RelativeLayout
android:id="@+id/volumeRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:visibility="visible">
<ProgressBar
android:id="@+id/volumeProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/volumeImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_volume_up_white_72dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/brightnessRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:visibility="visible">
<ProgressBar
android:id="@+id/brightnessProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/brightnessImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_brightness_high_white_72dp" />
</RelativeLayout>
<TextView
android:id="@+id/currentDisplaySeek"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginBottom="58dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="26sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="1:06:29"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>

Some files were not shown because too many files have changed in this diff Show more