merged upstream/dev
This commit is contained in:
commit
b6be586766
361 changed files with 16088 additions and 11403 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
liberapay: TeamNewPipe
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,7 +7,6 @@
|
||||||
/app/app.iml
|
/app/app.iml
|
||||||
/.idea
|
/.idea
|
||||||
/*.iml
|
/*.iml
|
||||||
gradle.properties
|
|
||||||
*~
|
*~
|
||||||
.weblate
|
.weblate
|
||||||
*.class
|
*.class
|
||||||
|
|
11
README.md
11
README.md
|
@ -66,15 +66,22 @@ NewPipe does not use any Google framework libraries, nor the YouTube API. Websit
|
||||||
* Enqueue videos
|
* Enqueue videos
|
||||||
* Local playlists
|
* Local playlists
|
||||||
* Subtitles
|
* Subtitles
|
||||||
* Multi-service support (e.g. SoundCloud \[beta\])
|
|
||||||
* Livestream support
|
* Livestream support
|
||||||
|
* Show comments
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
* Cast to UPnP and Cast
|
* Cast to UPnP and Cast
|
||||||
* Show comments
|
|
||||||
* … and many more
|
* … and many more
|
||||||
|
|
||||||
|
### Supported Services
|
||||||
|
|
||||||
|
NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/documentation/) provide more info on how a new service can be added to the app and the extractor. Please get in touch with us if you intend to add a new one. Currently supported services are:
|
||||||
|
|
||||||
|
* YouTube
|
||||||
|
* SoundCloud \[beta\]
|
||||||
|
* media.ccc.de \[beta\]
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can:
|
When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can:
|
||||||
* Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods.
|
* Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods.
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
|
@ -8,10 +11,10 @@ android {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 720
|
versionCode 790
|
||||||
versionName "0.16.0"
|
versionName "0.17.4"
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,38 +46,43 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
supportLibVersion = '28.0.0'
|
androidxLibVersion = '1.0.0'
|
||||||
exoPlayerLibVersion = '2.8.4' //2.9.0
|
exoPlayerLibVersion = '2.10.7'
|
||||||
roomDbLibVersion = '1.1.1'
|
roomDbLibVersion = '2.1.0'
|
||||||
leakCanaryLibVersion = '1.5.4' //1.6.1
|
leakCanaryLibVersion = '1.5.4' //1.6.1
|
||||||
okHttpLibVersion = '3.11.0'
|
okHttpLibVersion = '3.12.6'
|
||||||
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:3.0.2', {
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
|
||||||
|
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
})
|
})
|
||||||
|
|
||||||
implementation 'com.github.yausername:NewPipeExtractor:f60c973'
|
implementation 'com.github.yausername:NewPipeExtractor:318f600'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.mockito:mockito-core:2.23.0'
|
testImplementation 'org.mockito:mockito-core:2.23.0'
|
||||||
|
|
||||||
implementation "com.android.support:appcompat-v7:${supportLibVersion}"
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation "com.android.support:support-v4:${supportLibVersion}"
|
implementation "androidx.legacy:legacy-support-v4:${androidxLibVersion}"
|
||||||
implementation "com.android.support:design:${supportLibVersion}"
|
implementation "com.google.android.material:material:${androidxLibVersion}"
|
||||||
implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
|
implementation "androidx.recyclerview:recyclerview:${androidxLibVersion}"
|
||||||
implementation "com.android.support:preference-v14:${supportLibVersion}"
|
implementation "androidx.legacy:legacy-preference-v14:${androidxLibVersion}"
|
||||||
implementation "com.android.support:cardview-v7:${supportLibVersion}"
|
implementation "androidx.cardview:cardview:${androidxLibVersion}"
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
|
||||||
|
// Originally in NewPipeExtractor
|
||||||
|
implementation 'com.grack:nanojson:1.1'
|
||||||
|
implementation 'org.jsoup:jsoup:1.9.2'
|
||||||
|
|
||||||
implementation 'ch.acra:acra:4.9.2' //4.11
|
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.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}"
|
||||||
|
@ -82,18 +90,18 @@ dependencies {
|
||||||
|
|
||||||
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 'androidx.multidex:multidex:2.0.1'
|
||||||
|
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
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 "androidx.room:room-runtime:${roomDbLibVersion}"
|
||||||
implementation "android.arch.persistence.room:rxjava2:${roomDbLibVersion}"
|
implementation "androidx.room:room-rxjava2:${roomDbLibVersion}"
|
||||||
annotationProcessor "android.arch.persistence.room:compiler:${roomDbLibVersion}"
|
kapt "androidx.room:room-compiler:${roomDbLibVersion}"
|
||||||
|
|
||||||
implementation "frankiesardo:icepick:${icepickLibVersion}"
|
implementation "frankiesardo:icepick:${icepickLibVersion}"
|
||||||
annotationProcessor "frankiesardo:icepick-processor:${icepickLibVersion}"
|
kapt "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}"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.report;
|
package org.schabi.newpipe.report;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.support.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -3,8 +3,8 @@ package org.schabi.newpipe;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.multidex.MultiDex;
|
import androidx.multidex.MultiDex;
|
||||||
|
|
||||||
import com.facebook.stetho.Stetho;
|
import com.facebook.stetho.Stetho;
|
||||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
|
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
android:label="@string/reCaptchaActivity"/>
|
android:label="@string/reCaptchaActivity"/>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
|
@ -228,7 +228,20 @@
|
||||||
<data android:scheme="http"/>
|
<data android:scheme="http"/>
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
<data android:host="invidio.us"/>
|
<data android:host="invidio.us"/>
|
||||||
|
<data android:host="dev.invidio.us"/>
|
||||||
<data android:host="www.invidio.us"/>
|
<data android:host="www.invidio.us"/>
|
||||||
|
<data android:host="invidious.snopyta.org"/>
|
||||||
|
<data android:host="de.invidious.snopyta.org"/>
|
||||||
|
<data android:host="fi.invidious.snopyta.org"/>
|
||||||
|
<data android:host="vid.wxzm.sx"/>
|
||||||
|
<data android:host="invidious.kabi.tk"/>
|
||||||
|
<data android:host="invidiou.sh"/>
|
||||||
|
<data android:host="www.invidiou.sh"/>
|
||||||
|
<data android:host="no.invidiou.sh"/>
|
||||||
|
<data android:host="invidious.enkirton.net"/>
|
||||||
|
<data android:host="tube.poal.co"/>
|
||||||
|
<data android:host="invidious.13ad.de"/>
|
||||||
|
<data android:host="yt.elukerio.org"/>
|
||||||
<!-- video prefix -->
|
<!-- video prefix -->
|
||||||
<data android:pathPrefix="/embed/"/>
|
<data android:pathPrefix="/embed/"/>
|
||||||
<data android:pathPrefix="/watch"/>
|
<data android:pathPrefix="/watch"/>
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
package android.support.design.widget;
|
|
||||||
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.design.animation.AnimationUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
// check this https://github.com/ToDou/appbarlayout-spring-behavior/blob/master/appbarspring/src/main/java/android/support/design/widget/AppBarFlingFixBehavior.java
|
|
||||||
public final class FlingBehavior extends AppBarLayout.Behavior {
|
|
||||||
|
|
||||||
private ValueAnimator mOffsetAnimator;
|
|
||||||
private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms
|
|
||||||
|
|
||||||
public FlingBehavior() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlingBehavior(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
|
|
||||||
if (dy != 0) {
|
|
||||||
int val = child.getBottom();
|
|
||||||
if (val != 0) {
|
|
||||||
int min, max;
|
|
||||||
if (dy < 0) {
|
|
||||||
// We're scrolling down
|
|
||||||
} else {
|
|
||||||
// We're scrolling up
|
|
||||||
if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
|
|
||||||
mOffsetAnimator.cancel();
|
|
||||||
}
|
|
||||||
min = -child.getUpNestedPreScrollRange();
|
|
||||||
max = 0;
|
|
||||||
consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View target, float velocityX, float velocityY) {
|
|
||||||
|
|
||||||
if (velocityY != 0) {
|
|
||||||
if (velocityY < 0) {
|
|
||||||
// We're flinging down
|
|
||||||
int val = child.getBottom();
|
|
||||||
if (val != 0) {
|
|
||||||
final int targetScroll =
|
|
||||||
+child.getDownNestedPreScrollRange();
|
|
||||||
animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// We're flinging up
|
|
||||||
int val = child.getBottom();
|
|
||||||
if (val != 0) {
|
|
||||||
final int targetScroll = -child.getUpNestedPreScrollRange();
|
|
||||||
if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
|
|
||||||
animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
|
|
||||||
final AppBarLayout child, final int offset, float velocity) {
|
|
||||||
final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset);
|
|
||||||
|
|
||||||
final int duration;
|
|
||||||
velocity = Math.abs(velocity);
|
|
||||||
if (velocity > 0) {
|
|
||||||
duration = 3 * Math.round(1000 * (distance / velocity));
|
|
||||||
} else {
|
|
||||||
final float distanceRatio = (float) distance / child.getHeight();
|
|
||||||
duration = (int) ((distanceRatio + 1) * 150);
|
|
||||||
}
|
|
||||||
|
|
||||||
animateOffsetWithDuration(coordinatorLayout, child, offset, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout,
|
|
||||||
final AppBarLayout child, final int offset, final int duration) {
|
|
||||||
final int currentOffset = getTopBottomOffsetForScrollingSibling();
|
|
||||||
if (currentOffset == offset) {
|
|
||||||
if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
|
|
||||||
mOffsetAnimator.cancel();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mOffsetAnimator == null) {
|
|
||||||
mOffsetAnimator = new ValueAnimator();
|
|
||||||
mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
|
|
||||||
mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationUpdate(ValueAnimator animator) {
|
|
||||||
setHeaderTopBottomOffset(coordinatorLayout, child,
|
|
||||||
(Integer) animator.getAnimatedValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
mOffsetAnimator.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION));
|
|
||||||
mOffsetAnimator.setIntValues(currentOffset, offset);
|
|
||||||
mOffsetAnimator.start();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.google.android.material.appbar;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.OverScroller;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
// check this https://stackoverflow.com/questions/56849221/recyclerview-fling-causes-laggy-while-appbarlayout-is-scrolling/57997489#57997489
|
||||||
|
public final class FlingBehavior extends AppBarLayout.Behavior {
|
||||||
|
|
||||||
|
public FlingBehavior(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
|
||||||
|
switch (ev.getActionMasked()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
// remove reference to old nested scrolling child
|
||||||
|
resetNestedScrollingChild();
|
||||||
|
// Stop fling when your finger touches the screen
|
||||||
|
stopAppBarLayoutFling();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.onInterceptTouchEvent(parent, child, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private OverScroller getScrollerField() {
|
||||||
|
try {
|
||||||
|
Class<?> headerBehaviorType = this.getClass().getSuperclass().getSuperclass().getSuperclass();
|
||||||
|
if (headerBehaviorType != null) {
|
||||||
|
Field field = headerBehaviorType.getDeclaredField("scroller");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return ((OverScroller) field.get(this));
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
// ?
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Field getLastNestedScrollingChildRefField() {
|
||||||
|
try {
|
||||||
|
Class<?> headerBehaviorType = this.getClass().getSuperclass().getSuperclass();
|
||||||
|
if (headerBehaviorType != null) {
|
||||||
|
Field field = headerBehaviorType.getDeclaredField("lastNestedScrollingChildRef");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// ?
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetNestedScrollingChild(){
|
||||||
|
Field field = getLastNestedScrollingChildRefField();
|
||||||
|
if(field != null){
|
||||||
|
try {
|
||||||
|
Object value = field.get(this);
|
||||||
|
if(value != null) field.set(this, null);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
// ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAppBarLayoutFling() {
|
||||||
|
OverScroller scroller = getScrollerField();
|
||||||
|
if (scroller != null) scroller.forceFinished(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,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.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
||||||
|
|
|
@ -2,10 +2,10 @@ package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,18 @@ import android.net.ConnectivityManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import android.support.v4.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
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 java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
@ -37,7 +34,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +43,8 @@ import okhttp3.Response;
|
||||||
*/
|
*/
|
||||||
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
|
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName();
|
||||||
private static final Application app = App.getApp();
|
private static final Application app = App.getApp();
|
||||||
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
|
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
|
||||||
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
|
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
|
||||||
|
@ -90,9 +88,8 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
return response.body().string();
|
return response.body().string();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
ErrorActivity.reportError(app, ex, null, null,
|
// connectivity problems, do not alarm user and fail silently
|
||||||
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
|
if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
|
||||||
"app update API fail", R.string.app_ui_crash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -117,9 +114,8 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
|
||||||
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
|
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
|
||||||
|
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
ErrorActivity.reportError(app, ex, null, null,
|
// connectivity problems, do not alarm user and fail silently
|
||||||
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
|
if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
|
||||||
"could not parse app update JSON data", R.string.app_ui_crash));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.DownloadRequest;
|
import org.schabi.newpipe.extractor.DownloadRequest;
|
||||||
import org.schabi.newpipe.extractor.DownloadResponse;
|
import org.schabi.newpipe.extractor.DownloadResponse;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||||
|
@ -10,6 +12,7 @@ import org.schabi.newpipe.extractor.utils.Localization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -23,6 +26,8 @@ import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 28.01.16.
|
* Created by Christian Schabesberger on 28.01.16.
|
||||||
|
@ -163,7 +168,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
final ResponseBody body = response.body();
|
final ResponseBody body = response.body();
|
||||||
|
|
||||||
if (response.code() == 429) {
|
if (response.code() == 429) {
|
||||||
throw new ReCaptchaException("reCaptcha Challenge requested");
|
throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
|
@ -213,7 +218,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
final ResponseBody body = response.body();
|
final ResponseBody body = response.body();
|
||||||
|
|
||||||
if (response.code() == 429) {
|
if (response.code() == 429) {
|
||||||
throw new ReCaptchaException("reCaptcha Challenge requested");
|
throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
|
@ -221,7 +226,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DownloadResponse(body.string(), response.headers().toMultimap());
|
return new DownloadResponse(response.code(), body.string(), response.headers().toMultimap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -267,7 +272,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
final ResponseBody body = response.body();
|
final ResponseBody body = response.body();
|
||||||
|
|
||||||
if (response.code() == 429) {
|
if (response.code() == 429) {
|
||||||
throw new ReCaptchaException("reCaptcha Challenge requested");
|
throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
|
@ -275,6 +280,30 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DownloadResponse(body.string(), response.headers().toMultimap());
|
return new DownloadResponse(response.code(), body.string(), response.headers().toMultimap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadResponse head(String siteUrl) throws IOException, ReCaptchaException {
|
||||||
|
final Request request = new Request.Builder()
|
||||||
|
.head().url(siteUrl)
|
||||||
|
.addHeader("User-Agent", USER_AGENT)
|
||||||
|
.build();
|
||||||
|
final Response response = client.newCall(request).execute();
|
||||||
|
|
||||||
|
if (response.code() == 429) {
|
||||||
|
throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DownloadResponse(response.code(), null, response.headers().toMultimap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadResponse get(String siteUrl, @NonNull Localization localization) throws IOException, ReCaptchaException {
|
||||||
|
final Map<String, List<String>> requestHeaders = new HashMap<>();
|
||||||
|
requestHeaders.put("Accept-Language", singletonList(localization.getLanguage()));
|
||||||
|
|
||||||
|
return get(siteUrl, new DownloadRequest(null, requestHeaders));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -28,17 +28,7 @@ import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.design.widget.NavigationView;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.view.GravityCompat;
|
|
||||||
import android.support.v4.widget.DrawerLayout;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.ActionBarDrawerToggle;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -49,6 +39,17 @@ import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.view.GravityCompat;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -73,6 +74,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private DrawerLayout drawer = null;
|
private DrawerLayout drawer = null;
|
||||||
private NavigationView drawerItems = null;
|
private NavigationView drawerItems = null;
|
||||||
private TextView headerServiceView = null;
|
private TextView headerServiceView = null;
|
||||||
|
private Button toggleServiceButton = null;
|
||||||
|
|
||||||
private boolean servicesShown = false;
|
private boolean servicesShown = false;
|
||||||
private ImageView serviceArrow;
|
private ImageView serviceArrow;
|
||||||
|
@ -266,8 +268,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
serviceArrow = hView.findViewById(R.id.drawer_arrow);
|
serviceArrow = hView.findViewById(R.id.drawer_arrow);
|
||||||
headerServiceView = hView.findViewById(R.id.drawer_header_service_view);
|
headerServiceView = hView.findViewById(R.id.drawer_header_service_view);
|
||||||
Button action = hView.findViewById(R.id.drawer_header_action_button);
|
toggleServiceButton = hView.findViewById(R.id.drawer_header_action_button);
|
||||||
action.setOnClickListener(view -> {
|
toggleServiceButton.setOnClickListener(view -> {
|
||||||
toggleServices();
|
toggleServices();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -279,6 +281,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
drawerItems.getMenu().removeGroup(R.id.menu_tabs_group);
|
drawerItems.getMenu().removeGroup(R.id.menu_tabs_group);
|
||||||
drawerItems.getMenu().removeGroup(R.id.menu_options_about_group);
|
drawerItems.getMenu().removeGroup(R.id.menu_options_about_group);
|
||||||
|
|
||||||
|
|
||||||
if(servicesShown) {
|
if(servicesShown) {
|
||||||
showServices();
|
showServices();
|
||||||
} else {
|
} else {
|
||||||
|
@ -359,12 +362,14 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
// close drawer on return, and don't show animation, so its looks like the drawer isn't open
|
// close drawer on return, and don't show animation, so its looks like the drawer isn't open
|
||||||
// when the user returns to MainActivity
|
// when the user returns to MainActivity
|
||||||
drawer.closeDrawer(Gravity.START, false);
|
drawer.closeDrawer(GravityCompat.START, false);
|
||||||
try {
|
try {
|
||||||
String selectedServiceName = NewPipe.getService(
|
String selectedServiceName = NewPipe.getService(
|
||||||
ServiceHelper.getSelectedServiceId(this)).getServiceInfo().getName();
|
ServiceHelper.getSelectedServiceId(this)).getServiceInfo().getName();
|
||||||
headerServiceView.setText(selectedServiceName);
|
headerServiceView.setText(selectedServiceName);
|
||||||
headerServiceView.post(() -> headerServiceView.setSelected(true));
|
headerServiceView.post(() -> headerServiceView.setSelected(true));
|
||||||
|
toggleServiceButton.setContentDescription(
|
||||||
|
getString(R.string.drawer_header_description) + selectedServiceName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorActivity.reportUiError(this, e);
|
ErrorActivity.reportUiError(this, e);
|
||||||
}
|
}
|
||||||
|
@ -558,6 +563,14 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateDrawerHeaderString(String content) {
|
||||||
|
NavigationView navigationView = findViewById(R.id.navigation);
|
||||||
|
View hView = navigationView.getHeaderView(0);
|
||||||
|
Button action = hView.findViewById(R.id.drawer_header_action_button);
|
||||||
|
|
||||||
|
action.setContentDescription(content);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
private void handleIntent(Intent intent) {
|
||||||
try {
|
try {
|
||||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.arch.persistence.room.Room;
|
import androidx.room.Room;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.AppDatabase;
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,12 @@ import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.NavUtils;
|
import androidx.core.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.ValueCallback;
|
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
|
@ -37,15 +36,24 @@ import android.webkit.WebViewClient;
|
||||||
*/
|
*/
|
||||||
public class ReCaptchaActivity extends AppCompatActivity {
|
public class ReCaptchaActivity extends AppCompatActivity {
|
||||||
public static final int RECAPTCHA_REQUEST = 10;
|
public static final int RECAPTCHA_REQUEST = 10;
|
||||||
|
public static final String RECAPTCHA_URL_EXTRA = "recaptcha_url_extra";
|
||||||
|
|
||||||
public static final String TAG = ReCaptchaActivity.class.toString();
|
public static final String TAG = ReCaptchaActivity.class.toString();
|
||||||
public static final String YT_URL = "https://www.youtube.com";
|
public static final String YT_URL = "https://www.youtube.com";
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_recaptcha);
|
setContentView(R.layout.activity_recaptcha);
|
||||||
|
|
||||||
|
url = getIntent().getStringExtra(RECAPTCHA_URL_EXTRA);
|
||||||
|
if (url == null || url.isEmpty()) {
|
||||||
|
url = YT_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set return to Cancel by default
|
// Set return to Cancel by default
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
|
|
||||||
|
@ -73,15 +81,12 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
||||||
myWebView.clearHistory();
|
myWebView.clearHistory();
|
||||||
android.webkit.CookieManager cookieManager = CookieManager.getInstance();
|
android.webkit.CookieManager cookieManager = CookieManager.getInstance();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
cookieManager.removeAllCookies(new ValueCallback<Boolean>() {
|
cookieManager.removeAllCookies(aBoolean -> {});
|
||||||
@Override
|
|
||||||
public void onReceiveValue(Boolean aBoolean) {}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
cookieManager.removeAllCookie();
|
cookieManager.removeAllCookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
myWebView.loadUrl(YT_URL);
|
myWebView.loadUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReCaptchaWebViewClient extends WebViewClient {
|
private class ReCaptchaWebViewClient extends WebViewClient {
|
||||||
|
|
|
@ -9,12 +9,12 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -26,6 +26,8 @@ import android.widget.RadioButton;
|
||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
import org.schabi.newpipe.extractor.Info;
|
import org.schabi.newpipe.extractor.Info;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
@ -74,10 +76,13 @@ import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
|
||||||
*/
|
*/
|
||||||
public class RouterActivity extends AppCompatActivity {
|
public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@State protected int currentServiceId = -1;
|
@State
|
||||||
|
protected int currentServiceId = -1;
|
||||||
private StreamingService currentService;
|
private StreamingService currentService;
|
||||||
@State protected LinkType currentLinkType;
|
@State
|
||||||
@State protected int selectedRadioPosition = -1;
|
protected LinkType currentLinkType;
|
||||||
|
@State
|
||||||
|
protected int selectedRadioPosition = -1;
|
||||||
protected int selectedPreviously = -1;
|
protected int selectedPreviously = -1;
|
||||||
|
|
||||||
protected String currentUrl;
|
protected String currentUrl;
|
||||||
|
@ -430,7 +435,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
|
int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
|
||||||
sortedVideoStreams);
|
sortedVideoStreams);
|
||||||
|
|
||||||
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
|
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
|
||||||
downloadDialog.setVideoStreams(sortedVideoStreams);
|
downloadDialog.setVideoStreams(sortedVideoStreams);
|
||||||
downloadDialog.setAudioStreams(result.getAudioStreams());
|
downloadDialog.setAudioStreams(result.getAudioStreams());
|
||||||
|
@ -460,7 +465,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private static class AdapterChoiceItem {
|
private static class AdapterChoiceItem {
|
||||||
final String description, key;
|
final String description, key;
|
||||||
@DrawableRes final int icon;
|
@DrawableRes
|
||||||
|
final int icon;
|
||||||
|
|
||||||
AdapterChoiceItem(String key, String description, int icon) {
|
AdapterChoiceItem(String key, String description, int icon) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
@ -558,7 +564,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||||
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);;
|
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
|
||||||
|
;
|
||||||
|
|
||||||
PlayQueue playQueue;
|
PlayQueue playQueue;
|
||||||
String playerChoice = choice.playerChoice;
|
String playerChoice = choice.playerChoice;
|
||||||
|
@ -574,7 +581,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
playQueue = new SinglePlayQueue((StreamInfo) info);
|
playQueue = new SinglePlayQueue((StreamInfo) info);
|
||||||
|
|
||||||
if (playerChoice.equals(videoPlayerKey)) {
|
if (playerChoice.equals(videoPlayerKey)) {
|
||||||
NavigationHelper.playOnMainPlayer(this, playQueue);
|
NavigationHelper.playOnMainPlayer(this, playQueue, true);
|
||||||
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
|
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
|
||||||
} else if (playerChoice.equals(popupPlayerKey)) {
|
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||||
|
@ -587,11 +594,11 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
|
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
|
||||||
|
|
||||||
if (playerChoice.equals(videoPlayerKey)) {
|
if (playerChoice.equals(videoPlayerKey)) {
|
||||||
NavigationHelper.playOnMainPlayer(this, playQueue);
|
NavigationHelper.playOnMainPlayer(this, playQueue, true);
|
||||||
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
} else if (playerChoice.equals(backgroundPlayerKey)) {
|
||||||
NavigationHelper.playOnBackgroundPlayer(this, playQueue);
|
NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
|
||||||
} else if (playerChoice.equals(popupPlayerKey)) {
|
} else if (playerChoice.equals(popupPlayerKey)) {
|
||||||
NavigationHelper.playOnPopupPlayer(this, playQueue);
|
NavigationHelper.playOnPopupPlayer(this, playQueue, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,15 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -36,7 +38,6 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2),
|
new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2),
|
||||||
new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2),
|
new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2),
|
||||||
new SoftwareComponent("CircleImageView", "2014 - 2017", "Henning Dodenhof", "https://github.com/hdodenhof/CircleImageView", StandardLicenses.APACHE2),
|
new SoftwareComponent("CircleImageView", "2014 - 2017", "Henning Dodenhof", "https://github.com/hdodenhof/CircleImageView", StandardLicenses.APACHE2),
|
||||||
new SoftwareComponent("ParalaxScrollView", "2014", "Nir Hartmann", "https://github.com/nirhart/ParallaxScroll", StandardLicenses.MIT),
|
|
||||||
new SoftwareComponent("NoNonsense-FilePicker", "2016", "Jonas Kalderstam", "https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2),
|
new SoftwareComponent("NoNonsense-FilePicker", "2016", "Jonas Kalderstam", "https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2),
|
||||||
new SoftwareComponent("ExoPlayer", "2014-2017", "Google Inc", "https://github.com/google/ExoPlayer", StandardLicenses.APACHE2),
|
new SoftwareComponent("ExoPlayer", "2014-2017", "Google Inc", "https://github.com/google/ExoPlayer", StandardLicenses.APACHE2),
|
||||||
new SoftwareComponent("RxAndroid", "2015", "The RxAndroid authors", "https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2),
|
new SoftwareComponent("RxAndroid", "2015", "The RxAndroid authors", "https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2),
|
||||||
|
@ -45,12 +46,12 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link android.support.v4.view.PagerAdapter} that will provide
|
* The {@link PagerAdapter} that will provide
|
||||||
* fragments for each of the sections. We use a
|
* fragments for each of the sections. We use a
|
||||||
* {@link FragmentPagerAdapter} derivative, which will keep every
|
* {@link FragmentPagerAdapter} derivative, which will keep every
|
||||||
* loaded fragment in memory. If this becomes too memory intensive, it
|
* loaded fragment in memory. If this becomes too memory intensive, it
|
||||||
* may be best to switch to a
|
* may be best to switch to a
|
||||||
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
|
* {@link FragmentStatePagerAdapter}.
|
||||||
*/
|
*/
|
||||||
private SectionsPagerAdapter mSectionsPagerAdapter;
|
private SectionsPagerAdapter mSectionsPagerAdapter;
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
|
@ -4,8 +4,8 @@ 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.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.database;
|
package org.schabi.newpipe.database;
|
||||||
|
|
||||||
import android.arch.persistence.room.Database;
|
import androidx.room.Database;
|
||||||
import android.arch.persistence.room.RoomDatabase;
|
import androidx.room.RoomDatabase;
|
||||||
import android.arch.persistence.room.TypeConverters;
|
import androidx.room.TypeConverters;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
||||||
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database;
|
package org.schabi.newpipe.database;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Delete;
|
import androidx.room.Delete;
|
||||||
import android.arch.persistence.room.Insert;
|
import androidx.room.Insert;
|
||||||
import android.arch.persistence.room.OnConflictStrategy;
|
import androidx.room.OnConflictStrategy;
|
||||||
import android.arch.persistence.room.Update;
|
import androidx.room.Update;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.database;
|
package org.schabi.newpipe.database;
|
||||||
|
|
||||||
import android.arch.persistence.room.TypeConverter;
|
import androidx.room.TypeConverter;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
package org.schabi.newpipe.database;
|
package org.schabi.newpipe.database;
|
||||||
|
|
||||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||||
import android.arch.persistence.room.migration.Migration;
|
import androidx.room.migration.Migration;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.BuildConfig;
|
||||||
|
|
||||||
public class Migrations {
|
public class Migrations {
|
||||||
|
|
||||||
public static final int DB_VER_11_0 = 1;
|
public static final int DB_VER_11_0 = 1;
|
||||||
public static final int DB_VER_12_0 = 2;
|
public static final int DB_VER_12_0 = 2;
|
||||||
|
|
||||||
|
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||||
|
private static final String TAG = Migrations.class.getName();
|
||||||
|
|
||||||
public static final Migration MIGRATION_11_12 = new Migration(DB_VER_11_0, DB_VER_12_0) {
|
public static final Migration MIGRATION_11_12 = new Migration(DB_VER_11_0, DB_VER_12_0) {
|
||||||
@Override
|
@Override
|
||||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
|
if(DEBUG) {
|
||||||
|
Log.d(TAG, "Start migrating database");
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Unfortunately these queries must be hardcoded due to the possibility of
|
* Unfortunately these queries must be hardcoded due to the possibility of
|
||||||
* schema and names changing at a later date, thus invalidating the older migration
|
* schema and names changing at a later date, thus invalidating the older migration
|
||||||
|
@ -56,6 +65,10 @@ public class Migrations {
|
||||||
"ORDER BY creation_date DESC");
|
"ORDER BY creation_date DESC");
|
||||||
|
|
||||||
database.execSQL("DROP TABLE IF EXISTS watch_history");
|
database.execSQL("DROP TABLE IF EXISTS watch_history");
|
||||||
|
|
||||||
|
if(DEBUG) {
|
||||||
|
Log.d(TAG, "Stop migrating database");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.database.history.dao;
|
package org.schabi.newpipe.database.history.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.schabi.newpipe.database.history.dao;
|
package org.schabi.newpipe.database.history.dao;
|
||||||
|
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -50,6 +50,11 @@ public abstract class StreamHistoryDAO implements HistoryDAO<StreamHistoryEntity
|
||||||
" ORDER BY " + STREAM_ACCESS_DATE + " DESC")
|
" ORDER BY " + STREAM_ACCESS_DATE + " DESC")
|
||||||
public abstract Flowable<List<StreamHistoryEntry>> getHistory();
|
public abstract Flowable<List<StreamHistoryEntry>> getHistory();
|
||||||
|
|
||||||
|
@Query("SELECT * FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID +
|
||||||
|
" = :streamId ORDER BY " + STREAM_ACCESS_DATE + " DESC LIMIT 1")
|
||||||
|
@Nullable
|
||||||
|
public abstract StreamHistoryEntity getLatestEntry(final long streamId);
|
||||||
|
|
||||||
@Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
@Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
||||||
public abstract int deleteStreamHistory(final long streamId);
|
public abstract int deleteStreamHistory(final long streamId);
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.history.model;
|
package org.schabi.newpipe.database.history.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
package org.schabi.newpipe.database.history.model;
|
package org.schabi.newpipe.database.history.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.ForeignKey;
|
import androidx.room.ForeignKey;
|
||||||
import android.arch.persistence.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import static android.arch.persistence.room.ForeignKey.CASCADE;
|
import static androidx.room.ForeignKey.CASCADE;
|
||||||
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.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
|
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
|
||||||
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
|
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.database.history.model;
|
package org.schabi.newpipe.database.history.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.database.playlist;
|
package org.schabi.newpipe.database.playlist;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
|
|
||||||
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_NAME;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.database.playlist;
|
package org.schabi.newpipe.database.playlist;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.schabi.newpipe.database.playlist.dao;
|
package org.schabi.newpipe.database.playlist.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.database.playlist.dao;
|
package org.schabi.newpipe.database.playlist.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.arch.persistence.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.database.playlist.dao;
|
package org.schabi.newpipe.database.playlist.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.arch.persistence.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.schabi.newpipe.database.playlist.model;
|
package org.schabi.newpipe.database.playlist.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.playlist.model;
|
package org.schabi.newpipe.database.playlist.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package org.schabi.newpipe.database.playlist.model;
|
package org.schabi.newpipe.database.playlist.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.ForeignKey;
|
import androidx.room.ForeignKey;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
|
|
||||||
import static android.arch.persistence.room.ForeignKey.CASCADE;
|
import static androidx.room.ForeignKey.CASCADE;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID;
|
||||||
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID;
|
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.database.stream;
|
package org.schabi.newpipe.database.stream;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.stream.dao;
|
package org.schabi.newpipe.database.stream.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Insert;
|
import androidx.room.Insert;
|
||||||
import android.arch.persistence.room.OnConflictStrategy;
|
import androidx.room.OnConflictStrategy;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.arch.persistence.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.stream.dao;
|
package org.schabi.newpipe.database.stream.dao;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Insert;
|
import androidx.room.Insert;
|
||||||
import android.arch.persistence.room.OnConflictStrategy;
|
import androidx.room.OnConflictStrategy;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.arch.persistence.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.stream.model;
|
package org.schabi.newpipe.database.stream.model;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package org.schabi.newpipe.database.stream.model;
|
package org.schabi.newpipe.database.stream.model;
|
||||||
|
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.ForeignKey;
|
import androidx.room.ForeignKey;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import static android.arch.persistence.room.ForeignKey.CASCADE;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static androidx.room.ForeignKey.CASCADE;
|
||||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
|
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
|
||||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
|
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
|
||||||
|
|
||||||
|
@ -22,6 +25,12 @@ public class StreamStateEntity {
|
||||||
final public static String JOIN_STREAM_ID = "stream_id";
|
final public static String JOIN_STREAM_ID = "stream_id";
|
||||||
final public static String STREAM_PROGRESS_TIME = "progress_time";
|
final public static String STREAM_PROGRESS_TIME = "progress_time";
|
||||||
|
|
||||||
|
|
||||||
|
/** Playback state will not be saved, if playback time less than this threshold */
|
||||||
|
private static final int PLAYBACK_SAVE_THRESHOLD_START_SECONDS = 5;
|
||||||
|
/** Playback state will not be saved, if time left less than this threshold */
|
||||||
|
private static final int PLAYBACK_SAVE_THRESHOLD_END_SECONDS = 10;
|
||||||
|
|
||||||
@ColumnInfo(name = JOIN_STREAM_ID)
|
@ColumnInfo(name = JOIN_STREAM_ID)
|
||||||
private long streamUid;
|
private long streamUid;
|
||||||
|
|
||||||
|
@ -48,4 +57,18 @@ public class StreamStateEntity {
|
||||||
public void setProgressTime(long progressTime) {
|
public void setProgressTime(long progressTime) {
|
||||||
this.progressTime = progressTime;
|
this.progressTime = progressTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isValid(int durationInSeconds) {
|
||||||
|
final int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(progressTime);
|
||||||
|
return seconds > PLAYBACK_SAVE_THRESHOLD_START_SECONDS
|
||||||
|
&& seconds < durationInSeconds - PLAYBACK_SAVE_THRESHOLD_END_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
if (obj instanceof StreamStateEntity) {
|
||||||
|
return ((StreamStateEntity) obj).streamUid == streamUid
|
||||||
|
&& ((StreamStateEntity) obj).progressTime == progressTime;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package org.schabi.newpipe.database.subscription;
|
package org.schabi.newpipe.database.subscription;
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao;
|
import androidx.room.Dao;
|
||||||
import android.arch.persistence.room.Insert;
|
import androidx.room.Insert;
|
||||||
import android.arch.persistence.room.OnConflictStrategy;
|
import androidx.room.OnConflictStrategy;
|
||||||
import android.arch.persistence.room.Query;
|
import androidx.room.Query;
|
||||||
import android.arch.persistence.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.BasicDAO;
|
import org.schabi.newpipe.database.BasicDAO;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package org.schabi.newpipe.database.subscription;
|
package org.schabi.newpipe.database.subscription;
|
||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import android.arch.persistence.room.Entity;
|
import androidx.room.Entity;
|
||||||
import android.arch.persistence.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import android.arch.persistence.room.Index;
|
import androidx.room.Index;
|
||||||
import android.arch.persistence.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
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.channel.ChannelInfoItem;
|
||||||
|
|
|
@ -3,9 +3,9 @@ package org.schabi.newpipe.download;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -47,7 +47,7 @@ public class DownloadActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
updateFragments();
|
updateFragments();
|
||||||
getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -55,12 +55,13 @@ public class DownloadActivity extends AppCompatActivity {
|
||||||
private void updateFragments() {
|
private void updateFragments() {
|
||||||
MissionsFragment fragment = new MissionsFragment();
|
MissionsFragment fragment = new MissionsFragment();
|
||||||
|
|
||||||
getFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG)
|
.replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG)
|
||||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
@ -86,9 +87,4 @@ public class DownloadActivity extends AppCompatActivity {
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRestoreInstanceState(Bundle inState){
|
|
||||||
super.onRestoreInstanceState(inState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
package org.schabi.newpipe.download;
|
package org.schabi.newpipe.download;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.IdRes;
|
import androidx.annotation.IdRes;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
import androidx.annotation.StringRes;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.view.menu.ActionMenuItemView;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -24,6 +34,8 @@ import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nononsenseapps.filepicker.Utils;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
@ -34,7 +46,10 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
|
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.extractor.utils.Localization;
|
import org.schabi.newpipe.extractor.utils.Localization;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
|
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||||
import org.schabi.newpipe.util.FilenameUtils;
|
import org.schabi.newpipe.util.FilenameUtils;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
@ -43,19 +58,28 @@ import org.schabi.newpipe.util.StreamItemAdapter;
|
||||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import icepick.Icepick;
|
import icepick.Icepick;
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import us.shandian.giga.io.StoredDirectoryHelper;
|
||||||
|
import us.shandian.giga.io.StoredFileHelper;
|
||||||
import us.shandian.giga.postprocessing.Postprocessing;
|
import us.shandian.giga.postprocessing.Postprocessing;
|
||||||
|
import us.shandian.giga.service.DownloadManager;
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
|
import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder;
|
||||||
|
import us.shandian.giga.service.MissionState;
|
||||||
|
|
||||||
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
|
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
|
||||||
private static final String TAG = "DialogFragment";
|
private static final String TAG = "DialogFragment";
|
||||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
private static final int REQUEST_DOWNLOAD_SAVE_AS = 0x1230;
|
||||||
|
|
||||||
@State
|
@State
|
||||||
protected StreamInfo currentInfo;
|
protected StreamInfo currentInfo;
|
||||||
|
@ -80,7 +104,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
|
|
||||||
private EditText nameEditText;
|
private EditText nameEditText;
|
||||||
private Spinner streamsSpinner;
|
private Spinner streamsSpinner;
|
||||||
private RadioGroup radioVideoAudioGroup;
|
private RadioGroup radioStreamsGroup;
|
||||||
private TextView threadsCountTextView;
|
private TextView threadsCountTextView;
|
||||||
private SeekBar threadsSeekBar;
|
private SeekBar threadsSeekBar;
|
||||||
|
|
||||||
|
@ -155,12 +179,15 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
|
||||||
if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
|
if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
|
||||||
getDialog().dismiss();
|
getDialog().dismiss();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(getContext()));
|
context = getContext();
|
||||||
|
|
||||||
|
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
|
||||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||||
|
|
||||||
SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams = new SparseArray<>(4);
|
SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams = new SparseArray<>(4);
|
||||||
|
@ -177,9 +204,33 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.videoStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedVideoStreams, secondaryStreams);
|
this.videoStreamsAdapter = new StreamItemAdapter<>(context, wrappedVideoStreams, secondaryStreams);
|
||||||
this.audioStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedAudioStreams);
|
this.audioStreamsAdapter = new StreamItemAdapter<>(context, wrappedAudioStreams);
|
||||||
this.subtitleStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedSubtitleStreams);
|
this.subtitleStreamsAdapter = new StreamItemAdapter<>(context, wrappedSubtitleStreams);
|
||||||
|
|
||||||
|
Intent intent = new Intent(context, DownloadManagerService.class);
|
||||||
|
context.startService(intent);
|
||||||
|
|
||||||
|
context.bindService(intent, new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName cname, IBinder service) {
|
||||||
|
DownloadManagerBinder mgr = (DownloadManagerBinder) service;
|
||||||
|
|
||||||
|
mainStorageAudio = mgr.getMainStorageAudio();
|
||||||
|
mainStorageVideo = mgr.getMainStorageVideo();
|
||||||
|
downloadManager = mgr.getDownloadManager();
|
||||||
|
askForSavePath = mgr.askForSavePath();
|
||||||
|
|
||||||
|
okButton.setEnabled(true);
|
||||||
|
|
||||||
|
context.unbindService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
}, Context.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -204,8 +255,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
threadsCountTextView = view.findViewById(R.id.threads_count);
|
threadsCountTextView = view.findViewById(R.id.threads_count);
|
||||||
threadsSeekBar = view.findViewById(R.id.threads);
|
threadsSeekBar = view.findViewById(R.id.threads);
|
||||||
|
|
||||||
radioVideoAudioGroup = view.findViewById(R.id.video_audio_group);
|
radioStreamsGroup = view.findViewById(R.id.video_audio_group);
|
||||||
radioVideoAudioGroup.setOnCheckedChangeListener(this);
|
radioStreamsGroup.setOnCheckedChangeListener(this);
|
||||||
|
|
||||||
initToolbar(view.findViewById(R.id.toolbar));
|
initToolbar(view.findViewById(R.id.toolbar));
|
||||||
setupDownloadOptions();
|
setupDownloadOptions();
|
||||||
|
@ -240,17 +291,17 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
disposables.clear();
|
disposables.clear();
|
||||||
|
|
||||||
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams).subscribe(result -> {
|
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams).subscribe(result -> {
|
||||||
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.video_button) {
|
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.video_button) {
|
||||||
setupVideoSpinner();
|
setupVideoSpinner();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams).subscribe(result -> {
|
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams).subscribe(result -> {
|
||||||
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
|
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.audio_button) {
|
||||||
setupAudioSpinner();
|
setupAudioSpinner();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams).subscribe(result -> {
|
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams).subscribe(result -> {
|
||||||
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
|
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
|
||||||
setupSubtitleSpinner();
|
setupSubtitleSpinner();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -263,21 +314,55 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
Icepick.saveInstanceState(this, outState);
|
Icepick.saveInstanceState(this, outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (requestCode == REQUEST_DOWNLOAD_SAVE_AS && resultCode == Activity.RESULT_OK) {
|
||||||
|
if (data.getData() == null) {
|
||||||
|
showFailedDialog(R.string.general_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FilePickerActivityHelper.isOwnFileUri(context, data.getData())) {
|
||||||
|
File file = Utils.getFileForUri(data.getData());
|
||||||
|
checkSelectedDownload(null, Uri.fromFile(file), file.getName(), StoredFileHelper.DEFAULT_MIME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentFile docFile = DocumentFile.fromSingleUri(context, data.getData());
|
||||||
|
if (docFile == null) {
|
||||||
|
showFailedDialog(R.string.general_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the selected file was previously used
|
||||||
|
checkSelectedDownload(null, data.getData(), docFile.getName(), docFile.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Inits
|
// Inits
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void initToolbar(Toolbar toolbar) {
|
private void initToolbar(Toolbar toolbar) {
|
||||||
if (DEBUG) Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]");
|
if (DEBUG) Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]");
|
||||||
|
|
||||||
|
boolean isLight = ThemeHelper.isLightThemeSelected(getActivity());
|
||||||
|
|
||||||
toolbar.setTitle(R.string.download_dialog_title);
|
toolbar.setTitle(R.string.download_dialog_title);
|
||||||
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
toolbar.setNavigationIcon(isLight ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
toolbar.setNavigationOnClickListener(v -> getDialog().dismiss());
|
toolbar.setNavigationOnClickListener(v -> getDialog().dismiss());
|
||||||
|
toolbar.setNavigationContentDescription(R.string.cancel);
|
||||||
|
|
||||||
|
okButton = toolbar.findViewById(R.id.okay);
|
||||||
|
okButton.setEnabled(false);// disable until the download service connection is done
|
||||||
|
|
||||||
toolbar.setOnMenuItemClickListener(item -> {
|
toolbar.setOnMenuItemClickListener(item -> {
|
||||||
if (item.getItemId() == R.id.okay) {
|
if (item.getItemId() == R.id.okay) {
|
||||||
|
@ -346,7 +431,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
|
Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
|
||||||
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
|
switch (radioStreamsGroup.getCheckedRadioButtonId()) {
|
||||||
case R.id.audio_button:
|
case R.id.audio_button:
|
||||||
selectedAudioIndex = position;
|
selectedAudioIndex = position;
|
||||||
break;
|
break;
|
||||||
|
@ -370,9 +455,9 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
protected void setupDownloadOptions() {
|
protected void setupDownloadOptions() {
|
||||||
setRadioButtonsState(false);
|
setRadioButtonsState(false);
|
||||||
|
|
||||||
final RadioButton audioButton = radioVideoAudioGroup.findViewById(R.id.audio_button);
|
final RadioButton audioButton = radioStreamsGroup.findViewById(R.id.audio_button);
|
||||||
final RadioButton videoButton = radioVideoAudioGroup.findViewById(R.id.video_button);
|
final RadioButton videoButton = radioStreamsGroup.findViewById(R.id.video_button);
|
||||||
final RadioButton subtitleButton = radioVideoAudioGroup.findViewById(R.id.subtitle_button);
|
final RadioButton subtitleButton = radioStreamsGroup.findViewById(R.id.subtitle_button);
|
||||||
final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0;
|
final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0;
|
||||||
final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0;
|
final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0;
|
||||||
final boolean isSubtitleStreamsAvailable = subtitleStreamsAdapter.getCount() > 0;
|
final boolean isSubtitleStreamsAvailable = subtitleStreamsAdapter.getCount() > 0;
|
||||||
|
@ -397,9 +482,9 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRadioButtonsState(boolean enabled) {
|
private void setRadioButtonsState(boolean enabled) {
|
||||||
radioVideoAudioGroup.findViewById(R.id.audio_button).setEnabled(enabled);
|
radioStreamsGroup.findViewById(R.id.audio_button).setEnabled(enabled);
|
||||||
radioVideoAudioGroup.findViewById(R.id.video_button).setEnabled(enabled);
|
radioStreamsGroup.findViewById(R.id.video_button).setEnabled(enabled);
|
||||||
radioVideoAudioGroup.findViewById(R.id.subtitle_button).setEnabled(enabled);
|
radioStreamsGroup.findViewById(R.id.subtitle_button).setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSubtitleIndexBy(List<SubtitlesStream> streams) {
|
private int getSubtitleIndexBy(List<SubtitlesStream> streams) {
|
||||||
|
@ -434,92 +519,310 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StoredDirectoryHelper mainStorageAudio = null;
|
||||||
|
StoredDirectoryHelper mainStorageVideo = null;
|
||||||
|
DownloadManager downloadManager = null;
|
||||||
|
ActionMenuItemView okButton = null;
|
||||||
|
Context context;
|
||||||
|
boolean askForSavePath;
|
||||||
|
|
||||||
|
private String getNameEditText() {
|
||||||
|
String str = nameEditText.getText().toString().trim();
|
||||||
|
|
||||||
|
return FilenameUtils.createFilename(context, str.isEmpty() ? currentInfo.getName() : str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFailedDialog(@StringRes int msg) {
|
||||||
|
new AlertDialog.Builder(context)
|
||||||
|
.setTitle(R.string.general_error)
|
||||||
|
.setMessage(msg)
|
||||||
|
.setNegativeButton(android.R.string.ok, null)
|
||||||
|
.create()
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showErrorActivity(Exception e) {
|
||||||
|
ErrorActivity.reportError(
|
||||||
|
context,
|
||||||
|
Collections.singletonList(e),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "-", "-", R.string.general_error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareSelectedDownload() {
|
private void prepareSelectedDownload() {
|
||||||
final Context context = getContext();
|
StoredDirectoryHelper mainStorage;
|
||||||
Stream stream;
|
MediaFormat format;
|
||||||
String location;
|
String mime;
|
||||||
char kind;
|
|
||||||
|
|
||||||
String fileName = nameEditText.getText().toString().trim();
|
// first, build the filename and get the output folder (if possible)
|
||||||
if (fileName.isEmpty())
|
// later, run a very very very large file checking logic
|
||||||
fileName = FilenameUtils.createFilename(context, currentInfo.getName());
|
|
||||||
|
|
||||||
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
|
String filename = getNameEditText().concat(".");
|
||||||
|
|
||||||
|
switch (radioStreamsGroup.getCheckedRadioButtonId()) {
|
||||||
case R.id.audio_button:
|
case R.id.audio_button:
|
||||||
stream = audioStreamsAdapter.getItem(selectedAudioIndex);
|
mainStorage = mainStorageAudio;
|
||||||
location = NewPipeSettings.getAudioDownloadPath(context);
|
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
|
||||||
kind = 'a';
|
mime = format.mimeType;
|
||||||
|
filename += format.suffix;
|
||||||
break;
|
break;
|
||||||
case R.id.video_button:
|
case R.id.video_button:
|
||||||
stream = videoStreamsAdapter.getItem(selectedVideoIndex);
|
mainStorage = mainStorageVideo;
|
||||||
location = NewPipeSettings.getVideoDownloadPath(context);
|
format = videoStreamsAdapter.getItem(selectedVideoIndex).getFormat();
|
||||||
kind = 'v';
|
mime = format.mimeType;
|
||||||
|
filename += format.suffix;
|
||||||
break;
|
break;
|
||||||
case R.id.subtitle_button:
|
case R.id.subtitle_button:
|
||||||
stream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex);
|
mainStorage = mainStorageVideo;// subtitle & video files go together
|
||||||
location = NewPipeSettings.getVideoDownloadPath(context);// assume that subtitle & video files go together
|
format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat();
|
||||||
kind = 's';
|
mime = format.mimeType;
|
||||||
|
filename += format == MediaFormat.TTML ? MediaFormat.SRT.suffix : format.suffix;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("No stream selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainStorage == null || askForSavePath) {
|
||||||
|
// This part is called if with SAF preferred:
|
||||||
|
// * older android version running
|
||||||
|
// * save path not defined (via download settings)
|
||||||
|
// * the user checked the "ask where to download" option
|
||||||
|
|
||||||
|
if (!askForSavePath)
|
||||||
|
Toast.makeText(context, getString(R.string.no_available_dir), Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
if (NewPipeSettings.useStorageAccessFramework(context)) {
|
||||||
|
StoredFileHelper.requestSafWithFileCreation(this, REQUEST_DOWNLOAD_SAVE_AS, filename, mime);
|
||||||
|
} else {
|
||||||
|
File initialSavePath;
|
||||||
|
if (radioStreamsGroup.getCheckedRadioButtonId() == R.id.audio_button)
|
||||||
|
initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MUSIC);
|
||||||
|
else
|
||||||
|
initialSavePath = NewPipeSettings.getDir(Environment.DIRECTORY_MOVIES);
|
||||||
|
|
||||||
|
initialSavePath = new File(initialSavePath, filename);
|
||||||
|
startActivityForResult(
|
||||||
|
FilePickerActivityHelper.chooseFileToSave(context, initialSavePath.getAbsolutePath()),
|
||||||
|
REQUEST_DOWNLOAD_SAVE_AS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for existing file with the same name
|
||||||
|
checkSelectedDownload(mainStorage, mainStorage.findFile(filename), filename, mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkSelectedDownload(StoredDirectoryHelper mainStorage, Uri targetFile, String filename, String mime) {
|
||||||
|
StoredFileHelper storage;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (mainStorage == null) {
|
||||||
|
// using SAF on older android version
|
||||||
|
storage = new StoredFileHelper(context, null, targetFile, "");
|
||||||
|
} else if (targetFile == null) {
|
||||||
|
// the file does not exist, but it is probably used in a pending download
|
||||||
|
storage = new StoredFileHelper(mainStorage.getUri(), filename, mime, mainStorage.getTag());
|
||||||
|
} else {
|
||||||
|
// the target filename is already use, attempt to use it
|
||||||
|
storage = new StoredFileHelper(context, mainStorage.getUri(), targetFile, mainStorage.getTag());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
showErrorActivity(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if is our file
|
||||||
|
MissionState state = downloadManager.checkForExistingMission(storage);
|
||||||
|
@StringRes int msgBtn;
|
||||||
|
@StringRes int msgBody;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case Finished:
|
||||||
|
msgBtn = R.string.overwrite;
|
||||||
|
msgBody = R.string.overwrite_finished_warning;
|
||||||
|
break;
|
||||||
|
case Pending:
|
||||||
|
msgBtn = R.string.overwrite;
|
||||||
|
msgBody = R.string.download_already_pending;
|
||||||
|
break;
|
||||||
|
case PendingRunning:
|
||||||
|
msgBtn = R.string.generate_unique_name;
|
||||||
|
msgBody = R.string.download_already_running;
|
||||||
|
break;
|
||||||
|
case None:
|
||||||
|
if (mainStorage == null) {
|
||||||
|
// This part is called if:
|
||||||
|
// * using SAF on older android version
|
||||||
|
// * save path not defined
|
||||||
|
// * if the file exists overwrite it, is not necessary ask
|
||||||
|
if (!storage.existsAsFile() && !storage.create()) {
|
||||||
|
showFailedDialog(R.string.error_file_creation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continueSelectedDownload(storage);
|
||||||
|
return;
|
||||||
|
} else if (targetFile == null) {
|
||||||
|
// This part is called if:
|
||||||
|
// * the filename is not used in a pending/finished download
|
||||||
|
// * the file does not exists, create
|
||||||
|
|
||||||
|
if (!mainStorage.mkdirs()) {
|
||||||
|
showFailedDialog(R.string.error_path_creation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage = mainStorage.createFile(filename, mime);
|
||||||
|
if (storage == null || !storage.canWrite()) {
|
||||||
|
showFailedDialog(R.string.error_file_creation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
continueSelectedDownload(storage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msgBtn = R.string.overwrite;
|
||||||
|
msgBody = R.string.overwrite_unrelated_warning;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int threads;
|
|
||||||
|
|
||||||
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
|
AlertDialog.Builder askDialog = new AlertDialog.Builder(context)
|
||||||
threads = 1;// use unique thread for subtitles due small file size
|
.setTitle(R.string.download_dialog_title)
|
||||||
fileName += ".srt";// final subtitle format
|
.setMessage(msgBody)
|
||||||
} else {
|
.setNegativeButton(android.R.string.cancel, null);
|
||||||
threads = threadsSeekBar.getProgress() + 1;
|
final StoredFileHelper finalStorage = storage;
|
||||||
fileName += "." + stream.getFormat().getSuffix();
|
|
||||||
|
|
||||||
|
if (mainStorage == null) {
|
||||||
|
// This part is called if:
|
||||||
|
// * using SAF on older android version
|
||||||
|
// * save path not defined
|
||||||
|
switch (state) {
|
||||||
|
case Pending:
|
||||||
|
case Finished:
|
||||||
|
askDialog.setPositiveButton(msgBtn, (dialog, which) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
downloadManager.forgetMission(finalStorage);
|
||||||
|
continueSelectedDownload(finalStorage);
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String finalFileName = fileName;
|
askDialog.create().show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DownloadManagerService.checkForRunningMission(context, location, fileName, (listed, finished) -> {
|
askDialog.setPositiveButton(msgBtn, (dialog, which) -> {
|
||||||
if (listed) {
|
dialog.dismiss();
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setTitle(R.string.download_dialog_title)
|
StoredFileHelper storageNew;
|
||||||
.setMessage(finished ? R.string.overwrite_warning : R.string.download_already_running)
|
switch (state) {
|
||||||
.setPositiveButton(
|
case Finished:
|
||||||
finished ? R.string.overwrite : R.string.generate_unique_name,
|
case Pending:
|
||||||
(dialog, which) -> downloadSelected(context, stream, location, finalFileName, kind, threads)
|
downloadManager.forgetMission(finalStorage);
|
||||||
)
|
case None:
|
||||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
if (targetFile == null) {
|
||||||
dialog.cancel();
|
storageNew = mainStorage.createFile(filename, mime);
|
||||||
})
|
|
||||||
.create()
|
|
||||||
.show();
|
|
||||||
} else {
|
} else {
|
||||||
downloadSelected(context, stream, location, finalFileName, kind, threads);
|
try {
|
||||||
|
// try take (or steal) the file
|
||||||
|
storageNew = new StoredFileHelper(context, mainStorage.getUri(), targetFile, mainStorage.getTag());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to take (or steal) the file in " + targetFile.toString());
|
||||||
|
storageNew = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageNew != null && storageNew.canWrite())
|
||||||
|
continueSelectedDownload(storageNew);
|
||||||
|
else
|
||||||
|
showFailedDialog(R.string.error_file_creation);
|
||||||
|
break;
|
||||||
|
case PendingRunning:
|
||||||
|
storageNew = mainStorage.createUniqueFile(filename, mime);
|
||||||
|
if (storageNew == null)
|
||||||
|
showFailedDialog(R.string.error_file_creation);
|
||||||
|
else
|
||||||
|
continueSelectedDownload(storageNew);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
askDialog.create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadSelected(Context context, Stream selectedStream, String location, String fileName, char kind, int threads) {
|
private void continueSelectedDownload(@NonNull StoredFileHelper storage) {
|
||||||
|
if (!storage.canWrite()) {
|
||||||
|
showFailedDialog(R.string.permission_denied);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the selected file has to be overwritten, by simply checking its length
|
||||||
|
try {
|
||||||
|
if (storage.length() > 0) storage.truncate();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "failed to truncate the file: " + storage.getUri().toString(), e);
|
||||||
|
showFailedDialog(R.string.overwrite_failed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream selectedStream;
|
||||||
|
char kind;
|
||||||
|
int threads = threadsSeekBar.getProgress() + 1;
|
||||||
String[] urls;
|
String[] urls;
|
||||||
String psName = null;
|
String psName = null;
|
||||||
String[] psArgs = null;
|
String[] psArgs = null;
|
||||||
String secondaryStreamUrl = null;
|
String secondaryStreamUrl = null;
|
||||||
long nearLength = 0;
|
long nearLength = 0;
|
||||||
|
|
||||||
if (selectedStream instanceof VideoStream) {
|
// more download logic: select muxer, subtitle converter, etc.
|
||||||
|
switch (radioStreamsGroup.getCheckedRadioButtonId()) {
|
||||||
|
case R.id.audio_button:
|
||||||
|
kind = 'a';
|
||||||
|
selectedStream = audioStreamsAdapter.getItem(selectedAudioIndex);
|
||||||
|
|
||||||
|
if (selectedStream.getFormat() == MediaFormat.M4A) {
|
||||||
|
psName = Postprocessing.ALGORITHM_M4A_NO_DASH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.video_button:
|
||||||
|
kind = 'v';
|
||||||
|
selectedStream = videoStreamsAdapter.getItem(selectedVideoIndex);
|
||||||
|
|
||||||
SecondaryStreamHelper<AudioStream> secondaryStream = videoStreamsAdapter
|
SecondaryStreamHelper<AudioStream> secondaryStream = videoStreamsAdapter
|
||||||
.getAllSecondary()
|
.getAllSecondary()
|
||||||
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
|
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
|
||||||
|
|
||||||
if (secondaryStream != null) {
|
if (secondaryStream != null) {
|
||||||
secondaryStreamUrl = secondaryStream.getStream().getUrl();
|
secondaryStreamUrl = secondaryStream.getStream().getUrl();
|
||||||
psName = selectedStream.getFormat() == MediaFormat.MPEG_4 ? Postprocessing.ALGORITHM_MP4_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
|
|
||||||
|
if (selectedStream.getFormat() == MediaFormat.MPEG_4)
|
||||||
|
psName = Postprocessing.ALGORITHM_MP4_FROM_DASH_MUXER;
|
||||||
|
else
|
||||||
|
psName = Postprocessing.ALGORITHM_WEBM_MUXER;
|
||||||
|
|
||||||
psArgs = null;
|
psArgs = null;
|
||||||
long videoSize = wrappedVideoStreams.getSizeInBytes((VideoStream) selectedStream);
|
long videoSize = wrappedVideoStreams.getSizeInBytes((VideoStream) selectedStream);
|
||||||
|
|
||||||
// set nearLength, only, if both sizes are fetched or known. this probably does not work on slow networks
|
// set nearLength, only, if both sizes are fetched or known. This probably
|
||||||
|
// does not work on slow networks but is later updated in the downloader
|
||||||
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
|
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
|
||||||
nearLength = secondaryStream.getSizeInBytes() + videoSize;
|
nearLength = secondaryStream.getSizeInBytes() + videoSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((selectedStream instanceof SubtitlesStream) && selectedStream.getFormat() == MediaFormat.TTML) {
|
break;
|
||||||
|
case R.id.subtitle_button:
|
||||||
|
threads = 1;// use unique thread for subtitles due small file size
|
||||||
|
kind = 's';
|
||||||
|
selectedStream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex);
|
||||||
|
|
||||||
|
if (selectedStream.getFormat() == MediaFormat.TTML) {
|
||||||
psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
|
psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
|
||||||
psArgs = new String[]{
|
psArgs = new String[]{
|
||||||
selectedStream.getFormat().getSuffix(),
|
selectedStream.getFormat().getSuffix(),
|
||||||
|
@ -527,6 +830,10 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
"false",// detect youtube duplicate lines
|
"false",// detect youtube duplicate lines
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (secondaryStreamUrl == null) {
|
if (secondaryStreamUrl == null) {
|
||||||
urls = new String[]{selectedStream.getUrl()};
|
urls = new String[]{selectedStream.getUrl()};
|
||||||
|
@ -534,8 +841,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
urls = new String[]{selectedStream.getUrl(), secondaryStreamUrl};
|
urls = new String[]{selectedStream.getUrl(), secondaryStreamUrl};
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadManagerService.startMission(context, urls, location, fileName, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength);
|
DownloadManagerService.startMission(context, urls, storage, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength);
|
||||||
|
|
||||||
getDialog().dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package org.schabi.newpipe.fragments;
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
@ -180,7 +179,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception instanceof ReCaptchaException) {
|
if (exception instanceof ReCaptchaException) {
|
||||||
onReCaptchaException();
|
onReCaptchaException((ReCaptchaException) exception);
|
||||||
return true;
|
return true;
|
||||||
} else if (exception instanceof IOException) {
|
} else if (exception instanceof IOException) {
|
||||||
showError(getString(R.string.network_error), true);
|
showError(getString(R.string.network_error), true);
|
||||||
|
@ -190,11 +189,13 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReCaptchaException() {
|
public void onReCaptchaException(ReCaptchaException exception) {
|
||||||
if (DEBUG) Log.d(TAG, "onReCaptchaException() called");
|
if (DEBUG) Log.d(TAG, "onReCaptchaException() called");
|
||||||
Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
startActivityForResult(new Intent(activity, ReCaptchaActivity.class), ReCaptchaActivity.RECAPTCHA_REQUEST);
|
Intent intent = new Intent(activity, ReCaptchaActivity.class);
|
||||||
|
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, exception.getUrl());
|
||||||
|
startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST);
|
||||||
|
|
||||||
showError(getString(R.string.recaptcha_request_toast), false);
|
showError(getString(R.string.recaptcha_request_toast), false);
|
||||||
}
|
}
|
||||||
|
@ -230,21 +231,4 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
|
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utils
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
protected void openUrlInBrowser(String url) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
||||||
startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void shareUrl(String subject, String url) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, url);
|
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.schabi.newpipe.fragments;
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.schabi.newpipe.fragments;
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
package org.schabi.newpipe.fragments;
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.design.widget.TabLayout;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -18,6 +9,17 @@ import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
import org.schabi.newpipe.BaseFragment;
|
import org.schabi.newpipe.BaseFragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
@ -50,6 +52,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
destroyOldFragments();
|
||||||
|
|
||||||
tabsManager = TabsManager.getManager(activity);
|
tabsManager = TabsManager.getManager(activity);
|
||||||
tabsManager.setSavedTabsListener(() -> {
|
tabsManager.setSavedTabsListener(() -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
@ -63,6 +67,17 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void destroyOldFragments() {
|
||||||
|
for (Fragment fragment : getChildFragmentManager().getFragments()) {
|
||||||
|
if (fragment != null) {
|
||||||
|
getChildFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.remove(fragment)
|
||||||
|
.commitNowAllowingStateLoss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||||
|
@ -98,6 +113,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
tabsManager.unsetSavedTabsListener();
|
tabsManager.unsetSavedTabsListener();
|
||||||
|
if (viewPager != null) viewPager.setAdapter(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -144,6 +160,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
|
|
||||||
viewPager.setOffscreenPageLimit(pagerAdapter.getCount());
|
viewPager.setOffscreenPageLimit(pagerAdapter.getCount());
|
||||||
updateTabsIcon();
|
updateTabsIcon();
|
||||||
|
updateTabsContentDescription();
|
||||||
updateCurrentTitle();
|
updateCurrentTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +173,17 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateTabsContentDescription() {
|
||||||
|
for (int i = 0; i < tabsList.size(); i++) {
|
||||||
|
final TabLayout.Tab tabToSet = tabLayout.getTabAt(i);
|
||||||
|
if (tabToSet != null) {
|
||||||
|
final Tab t = tabsList.get(i);
|
||||||
|
tabToSet.setIcon(t.getTabIconRes(activity));
|
||||||
|
tabToSet.setContentDescription(t.getTabName(activity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateCurrentTitle() {
|
private void updateCurrentTitle() {
|
||||||
setTitle(tabsList.get(viewPager.getCurrentItem()).getTabName(requireContext()));
|
setTitle(tabsList.get(viewPager.getCurrentItem()).getTabName(requireContext()));
|
||||||
}
|
}
|
||||||
|
@ -177,6 +205,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectedTabsPagerAdapter extends FragmentPagerAdapter {
|
private class SelectedTabsPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private SelectedTabsPagerAdapter(FragmentManager fragmentManager) {
|
private SelectedTabsPagerAdapter(FragmentManager fragmentManager) {
|
||||||
super(fragmentManager);
|
super(fragmentManager);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.schabi.newpipe.fragments;
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.support.v7.widget.StaggeredGridLayoutManager;
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recycler view scroll listener which calls the method {@link #onScrolledDown(RecyclerView)}
|
* Recycler view scroll listener which calls the method {@link #onScrolledDown(RecyclerView)}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package org.schabi.newpipe.fragments.detail;
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -61,6 +62,18 @@ public class TabAdaptor extends FragmentPagerAdapter {
|
||||||
else return POSITION_NONE;
|
else return POSITION_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getItemPositionByTitle(String title) {
|
||||||
|
return mFragmentTitleList.indexOf(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getItemTitle(int position) {
|
||||||
|
if (position < 0 || position >= mFragmentTitleList.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mFragmentTitleList.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
public void notifyDataSetUpdate(){
|
public void notifyDataSetUpdate(){
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,17 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.design.widget.AppBarLayout;
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
import android.support.design.widget.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.support.v4.view.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -60,7 +60,6 @@ import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExt
|
||||||
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;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
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;
|
||||||
|
@ -68,7 +67,6 @@ import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.fragments.EmptyFragment;
|
import org.schabi.newpipe.fragments.EmptyFragment;
|
||||||
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
||||||
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
|
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
|
@ -77,7 +75,6 @@ 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.ErrorActivity;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
|
@ -86,14 +83,16 @@ import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.ShareUtils;
|
||||||
import org.schabi.newpipe.util.StreamItemAdapter;
|
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||||
|
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
@ -117,11 +116,12 @@ public class VideoDetailFragment
|
||||||
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
|
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
|
||||||
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
|
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
|
||||||
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
|
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
|
||||||
private static final int COMMENTS_UPDATE_FLAG = 0x4;
|
private static final int COMMENTS_UPDATE_FLAG = 0x8;
|
||||||
|
|
||||||
private boolean autoPlayEnabled;
|
private boolean autoPlayEnabled;
|
||||||
private boolean showRelatedStreams;
|
private boolean showRelatedStreams;
|
||||||
private boolean showComments;
|
private boolean showComments;
|
||||||
|
private String selectedTabTag;
|
||||||
|
|
||||||
@State
|
@State
|
||||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
protected int serviceId = Constants.NO_SERVICE_ID;
|
||||||
|
@ -134,6 +134,8 @@ public class VideoDetailFragment
|
||||||
private Disposable currentWorker;
|
private Disposable currentWorker;
|
||||||
@NonNull
|
@NonNull
|
||||||
private CompositeDisposable disposables = new CompositeDisposable();
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
@Nullable
|
||||||
|
private Disposable positionSubscriber = null;
|
||||||
|
|
||||||
private List<VideoStream> sortedVideoStreams;
|
private List<VideoStream> sortedVideoStreams;
|
||||||
private int selectedVideoStreamIndex = -1;
|
private int selectedVideoStreamIndex = -1;
|
||||||
|
@ -151,6 +153,7 @@ public class VideoDetailFragment
|
||||||
private View thumbnailBackgroundButton;
|
private View thumbnailBackgroundButton;
|
||||||
private ImageView thumbnailImageView;
|
private ImageView thumbnailImageView;
|
||||||
private ImageView thumbnailPlayButton;
|
private ImageView thumbnailPlayButton;
|
||||||
|
private AnimatedProgressBar positionView;
|
||||||
|
|
||||||
private View videoTitleRoot;
|
private View videoTitleRoot;
|
||||||
private TextView videoTitleTextView;
|
private TextView videoTitleTextView;
|
||||||
|
@ -163,6 +166,7 @@ public class VideoDetailFragment
|
||||||
private TextView detailControlsDownload;
|
private TextView detailControlsDownload;
|
||||||
private TextView appendControlsDetail;
|
private TextView appendControlsDetail;
|
||||||
private TextView detailDurationView;
|
private TextView detailDurationView;
|
||||||
|
private TextView detailPositionView;
|
||||||
|
|
||||||
private LinearLayout videoDescriptionRootLayout;
|
private LinearLayout videoDescriptionRootLayout;
|
||||||
private TextView videoUploadDateView;
|
private TextView videoUploadDateView;
|
||||||
|
@ -213,6 +217,9 @@ public class VideoDetailFragment
|
||||||
showComments = PreferenceManager.getDefaultSharedPreferences(activity)
|
showComments = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(getString(R.string.show_comments_key), true);
|
.getBoolean(getString(R.string.show_comments_key), true);
|
||||||
|
|
||||||
|
selectedTabTag = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
.getString(getString(R.string.stream_info_selected_tab_key), COMMENTS_TAB_TAG);
|
||||||
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.registerOnSharedPreferenceChangeListener(this);
|
.registerOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
@ -226,6 +233,10 @@ public class VideoDetailFragment
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (currentWorker != null) currentWorker.dispose();
|
if (currentWorker != null) currentWorker.dispose();
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||||
|
.edit()
|
||||||
|
.putString(getString(R.string.stream_info_selected_tab_key), pageAdapter.getItemTitle(viewPager.getCurrentItem()))
|
||||||
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -250,6 +261,8 @@ public class VideoDetailFragment
|
||||||
// Check if it was loading when the fragment was stopped/paused,
|
// Check if it was loading when the fragment was stopped/paused,
|
||||||
if (wasLoading.getAndSet(false)) {
|
if (wasLoading.getAndSet(false)) {
|
||||||
selectAndLoadVideo(serviceId, url, name);
|
selectAndLoadVideo(serviceId, url, name);
|
||||||
|
} else if (currentInfo != null) {
|
||||||
|
updateProgressInfo(currentInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,8 +272,10 @@ public class VideoDetailFragment
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.unregisterOnSharedPreferenceChangeListener(this);
|
.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
if (positionSubscriber != null) positionSubscriber.dispose();
|
||||||
if (currentWorker != null) currentWorker.dispose();
|
if (currentWorker != null) currentWorker.dispose();
|
||||||
if (disposables != null) disposables.clear();
|
if (disposables != null) disposables.clear();
|
||||||
|
positionSubscriber = null;
|
||||||
currentWorker = null;
|
currentWorker = null;
|
||||||
disposables = null;
|
disposables = null;
|
||||||
}
|
}
|
||||||
|
@ -453,6 +468,7 @@ public class VideoDetailFragment
|
||||||
videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view);
|
videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view);
|
||||||
videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_view);
|
videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_view);
|
||||||
videoCountView = rootView.findViewById(R.id.detail_view_count_view);
|
videoCountView = rootView.findViewById(R.id.detail_view_count_view);
|
||||||
|
positionView = rootView.findViewById(R.id.position_view);
|
||||||
|
|
||||||
detailControlsBackground = rootView.findViewById(R.id.detail_controls_background);
|
detailControlsBackground = rootView.findViewById(R.id.detail_controls_background);
|
||||||
detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup);
|
detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup);
|
||||||
|
@ -460,6 +476,7 @@ public class VideoDetailFragment
|
||||||
detailControlsDownload = rootView.findViewById(R.id.detail_controls_download);
|
detailControlsDownload = rootView.findViewById(R.id.detail_controls_download);
|
||||||
appendControlsDetail = rootView.findViewById(R.id.touch_append_detail);
|
appendControlsDetail = rootView.findViewById(R.id.touch_append_detail);
|
||||||
detailDurationView = rootView.findViewById(R.id.detail_duration_view);
|
detailDurationView = rootView.findViewById(R.id.detail_duration_view);
|
||||||
|
detailPositionView = rootView.findViewById(R.id.detail_position_view);
|
||||||
|
|
||||||
videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
|
videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
|
||||||
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
|
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
|
||||||
|
@ -467,7 +484,6 @@ public class VideoDetailFragment
|
||||||
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
videoDescriptionView.setAutoLinkMask(Linkify.WEB_URLS);
|
videoDescriptionView.setAutoLinkMask(Linkify.WEB_URLS);
|
||||||
|
|
||||||
//thumbsRootLayout = rootView.findViewById(R.id.detail_thumbs_root_layout);
|
|
||||||
thumbsUpTextView = rootView.findViewById(R.id.detail_thumbs_up_count_view);
|
thumbsUpTextView = rootView.findViewById(R.id.detail_thumbs_up_count_view);
|
||||||
thumbsUpImageView = rootView.findViewById(R.id.detail_thumbs_up_img_view);
|
thumbsUpImageView = rootView.findViewById(R.id.detail_thumbs_up_img_view);
|
||||||
thumbsDownTextView = rootView.findViewById(R.id.detail_thumbs_down_count_view);
|
thumbsDownTextView = rootView.findViewById(R.id.detail_thumbs_down_count_view);
|
||||||
|
@ -513,42 +529,6 @@ public class VideoDetailFragment
|
||||||
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStreamDialog(final StreamInfoItem item) {
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
|
||||||
context.getResources().getString(R.string.append_playlist),
|
|
||||||
context.getResources().getString(R.string.share)
|
|
||||||
};
|
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (getFragmentManager() != null) {
|
|
||||||
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
|
|
||||||
.show(getFragmentManager(), TAG);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
shareUrl(item.getName(), item.getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private View.OnTouchListener getOnControlsTouchListener() {
|
private View.OnTouchListener getOnControlsTouchListener() {
|
||||||
return (View view, MotionEvent motionEvent) -> {
|
return (View view, MotionEvent motionEvent) -> {
|
||||||
if (!PreferenceManager.getDefaultSharedPreferences(activity)
|
if (!PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
@ -628,13 +608,13 @@ public class VideoDetailFragment
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case R.id.menu_item_share: {
|
case R.id.menu_item_share: {
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
shareUrl(currentInfo.getName(), currentInfo.getOriginalUrl());
|
ShareUtils.shareUrl(this.getContext(), currentInfo.getName(), currentInfo.getOriginalUrl());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.menu_item_openInBrowser: {
|
case R.id.menu_item_openInBrowser: {
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
openUrlInBrowser(currentInfo.getOriginalUrl());
|
ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -815,6 +795,9 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTabs() {
|
private void initTabs() {
|
||||||
|
if (pageAdapter.getCount() != 0) {
|
||||||
|
selectedTabTag = pageAdapter.getItemTitle(viewPager.getCurrentItem());
|
||||||
|
}
|
||||||
pageAdapter.clearAllItems();
|
pageAdapter.clearAllItems();
|
||||||
|
|
||||||
if(shouldShowComments()){
|
if(shouldShowComments()){
|
||||||
|
@ -835,6 +818,8 @@ public class VideoDetailFragment
|
||||||
if(pageAdapter.getCount() < 2){
|
if(pageAdapter.getCount() < 2){
|
||||||
tabLayout.setVisibility(View.GONE);
|
tabLayout.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
|
int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
|
||||||
|
if(position != -1) viewPager.setCurrentItem(position);
|
||||||
tabLayout.setVisibility(View.VISIBLE);
|
tabLayout.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -876,11 +861,11 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||||
if (append) {
|
if (append) {
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue);
|
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue, false);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution
|
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution, true
|
||||||
);
|
);
|
||||||
activity.startService(intent);
|
activity.startService(intent);
|
||||||
}
|
}
|
||||||
|
@ -900,9 +885,9 @@ public class VideoDetailFragment
|
||||||
private void openNormalBackgroundPlayer(final boolean append) {
|
private void openNormalBackgroundPlayer(final boolean append) {
|
||||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||||
if (append) {
|
if (append) {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue);
|
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue, false);
|
||||||
} else {
|
} else {
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue);
|
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,7 +897,7 @@ public class VideoDetailFragment
|
||||||
mIntent = NavigationHelper.getPlayerIntent(activity,
|
mIntent = NavigationHelper.getPlayerIntent(activity,
|
||||||
MainVideoPlayer.class,
|
MainVideoPlayer.class,
|
||||||
playQueue,
|
playQueue,
|
||||||
getSelectedVideoStream().getResolution());
|
getSelectedVideoStream().getResolution(), true);
|
||||||
startActivity(mIntent);
|
startActivity(mIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,7 +964,7 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showContent() {
|
private void showContent() {
|
||||||
AnimationUtils.slideUp(contentRootLayoutHiding,120, 96, 0.06f);
|
contentRootLayoutHiding.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setInitialData(int serviceId, String url, String name) {
|
protected void setInitialData(int serviceId, String url, String name) {
|
||||||
|
@ -1012,12 +997,19 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showLoading() {
|
public void showLoading() {
|
||||||
|
|
||||||
super.showLoading();
|
super.showLoading();
|
||||||
|
|
||||||
|
//if data is already cached, transition from VISIBLE -> INVISIBLE -> VISIBLE is not required
|
||||||
|
if(!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)){
|
||||||
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
|
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
animateView(spinnerToolbar, false, 200);
|
animateView(spinnerToolbar, false, 200);
|
||||||
animateView(thumbnailPlayButton, false, 50);
|
animateView(thumbnailPlayButton, false, 50);
|
||||||
animateView(detailDurationView, false, 100);
|
animateView(detailDurationView, false, 100);
|
||||||
|
animateView(detailPositionView, false, 100);
|
||||||
|
animateView(positionView, false, 50);
|
||||||
|
|
||||||
videoTitleTextView.setText(name != null ? name : "");
|
videoTitleTextView.setText(name != null ? name : "");
|
||||||
videoTitleTextView.setMaxLines(1);
|
videoTitleTextView.setMaxLines(1);
|
||||||
|
@ -1132,6 +1124,7 @@ public class VideoDetailFragment
|
||||||
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
|
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
|
||||||
}
|
}
|
||||||
prepareDescription(info.getDescription());
|
prepareDescription(info.getDescription());
|
||||||
|
updateProgressInfo(info);
|
||||||
|
|
||||||
animateView(spinnerToolbar, true, 500);
|
animateView(spinnerToolbar, true, 500);
|
||||||
setupActionBar(info);
|
setupActionBar(info);
|
||||||
|
@ -1181,7 +1174,7 @@ public class VideoDetailFragment
|
||||||
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
||||||
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
|
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
|
||||||
|
|
||||||
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
downloadDialog.show(getActivity().getSupportFragmentManager(), "downloadDialog");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
||||||
ServiceList.all()
|
ServiceList.all()
|
||||||
|
@ -1206,9 +1199,7 @@ public class VideoDetailFragment
|
||||||
protected boolean onError(Throwable exception) {
|
protected boolean onError(Throwable exception) {
|
||||||
if (super.onError(exception)) return true;
|
if (super.onError(exception)) return true;
|
||||||
|
|
||||||
if (exception instanceof YoutubeStreamExtractor.GemaException) {
|
else if (exception instanceof ContentNotAvailableException) {
|
||||||
onBlockedByGemaError();
|
|
||||||
} else if (exception instanceof ContentNotAvailableException) {
|
|
||||||
showError(getString(R.string.content_not_available), false);
|
showError(getString(R.string.content_not_available), false);
|
||||||
} else {
|
} else {
|
||||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
|
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
|
||||||
|
@ -1226,14 +1217,36 @@ public class VideoDetailFragment
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBlockedByGemaError() {
|
private void updateProgressInfo(@NonNull final StreamInfo info) {
|
||||||
thumbnailBackgroundButton.setOnClickListener((View v) -> {
|
if (positionSubscriber != null) {
|
||||||
Intent intent = new Intent();
|
positionSubscriber.dispose();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
}
|
||||||
intent.setData(Uri.parse(getString(R.string.c3s_url)));
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
startActivity(intent);
|
final boolean playbackResumeEnabled =
|
||||||
|
prefs.getBoolean(activity.getString(R.string.enable_watch_history_key), true)
|
||||||
|
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
|
||||||
|
if (!playbackResumeEnabled || info.getDuration() <= 0) {
|
||||||
|
positionView.setVisibility(View.INVISIBLE);
|
||||||
|
detailPositionView.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
|
||||||
|
positionSubscriber = recordManager.loadStreamState(info)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.onErrorComplete()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(state -> {
|
||||||
|
final int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime());
|
||||||
|
positionView.setMax((int) info.getDuration());
|
||||||
|
positionView.setProgressAnimated(seconds);
|
||||||
|
detailPositionView.setText(Localization.getDurationString(seconds));
|
||||||
|
animateView(positionView, true, 500);
|
||||||
|
animateView(detailPositionView, true, 500);
|
||||||
|
}, e -> {
|
||||||
|
if (DEBUG) e.printStackTrace();
|
||||||
|
}, () -> {
|
||||||
|
animateView(positionView, false, 500);
|
||||||
|
animateView(detailPositionView, false, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,17 @@ 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.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -25,18 +24,17 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
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.extractor.stream.StreamType;
|
||||||
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.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.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
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.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
@ -64,6 +62,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
infoListAdapter = new InfoListAdapter(activity);
|
infoListAdapter = new InfoListAdapter(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -249,41 +252,32 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
if (context == null || context.getResources() == null || activity == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
StreamDialogEntry.setEnabledEntries(
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
StreamDialogEntry.enqueue_on_background,
|
||||||
context.getResources().getString(R.string.append_playlist),
|
StreamDialogEntry.start_here_on_background,
|
||||||
context.getResources().getString(R.string.share)
|
StreamDialogEntry.append_playlist,
|
||||||
};
|
StreamDialogEntry.share);
|
||||||
|
} else {
|
||||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
StreamDialogEntry.setEnabledEntries(
|
||||||
switch (i) {
|
StreamDialogEntry.enqueue_on_background,
|
||||||
case 0:
|
StreamDialogEntry.enqueue_on_popup,
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
StreamDialogEntry.start_here_on_background,
|
||||||
break;
|
StreamDialogEntry.start_here_on_popup,
|
||||||
case 1:
|
StreamDialogEntry.append_playlist,
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
StreamDialogEntry.share);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (getFragmentManager() != null) {
|
|
||||||
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
|
|
||||||
.show(getFragmentManager(), TAG);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
shareUrl(item.getName(), item.getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||||
|
StreamDialogEntry.clickOn(which, this, item)).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package org.schabi.newpipe.fragments.list;
|
package org.schabi.newpipe.fragments.list;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.ListInfo;
|
import org.schabi.newpipe.extractor.ListInfo;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
@ -61,9 +62,11 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (currentWorker != null) currentWorker.dispose();
|
if (currentWorker != null) {
|
||||||
|
currentWorker.dispose();
|
||||||
currentWorker = null;
|
currentWorker = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// State Saving
|
// State Saving
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package org.schabi.newpipe.fragments.list.channel;
|
package org.schabi.newpipe.fragments.list.channel;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -35,21 +33,18 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
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.local.dialog.PlaylistAppendDialog;
|
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||||
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;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.ShareUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -150,56 +145,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
return headerRootLayout;
|
return headerRootLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
|
||||||
context.getResources().getString(R.string.start_here_on_main),
|
|
||||||
context.getResources().getString(R.string.start_here_on_background),
|
|
||||||
context.getResources().getString(R.string.start_here_on_popup),
|
|
||||||
context.getResources().getString(R.string.append_playlist),
|
|
||||||
context.getResources().getString(R.string.share)
|
|
||||||
};
|
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
|
|
||||||
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
if (getFragmentManager() != null) {
|
|
||||||
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
|
|
||||||
.show(getFragmentManager(), TAG);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
shareUrl(item.getName(), item.getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
|
||||||
}
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -234,10 +179,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
openRssFeed();
|
openRssFeed();
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_openInBrowser:
|
case R.id.menu_item_openInBrowser:
|
||||||
openUrlInBrowser(currentInfo.getOriginalUrl());
|
ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl());
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_share:
|
case R.id.menu_item_share:
|
||||||
shareUrl(name, currentInfo.getOriginalUrl());
|
ShareUtils.shareUrl(this.getContext(), name, currentInfo.getOriginalUrl());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -440,11 +385,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
monitorSubscription(result);
|
monitorSubscription(result);
|
||||||
|
|
||||||
headerPlayAllButton.setOnClickListener(
|
headerPlayAllButton.setOnClickListener(
|
||||||
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||||
headerPopupButton.setOnClickListener(
|
headerPopupButton.setOnClickListener(
|
||||||
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||||
headerBackgroundButton.setOnClickListener(
|
headerBackgroundButton.setOnClickListener(
|
||||||
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlayQueue getPlayQueue() {
|
private PlayQueue getPlayQueue() {
|
||||||
|
|
|
@ -2,8 +2,8 @@ package org.schabi.newpipe.fragments.list.comments;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -93,7 +93,7 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
||||||
public void handleResult(@NonNull CommentsInfo result) {
|
public void handleResult(@NonNull CommentsInfo result) {
|
||||||
super.handleResult(result);
|
super.handleResult(result);
|
||||||
|
|
||||||
AnimationUtils.slideUp(getView(),120, 96, 0.06f);
|
AnimationUtils.slideUp(getView(),120, 150, 0.06f);
|
||||||
|
|
||||||
if (!result.getErrors().isEmpty()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package org.schabi.newpipe.fragments.list.kiosk;
|
package org.schabi.newpipe.fragments.list.kiosk;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -155,9 +154,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||||
super.handleResult(result);
|
super.handleResult(result);
|
||||||
|
|
||||||
name = kioskTranslatedName;
|
name = kioskTranslatedName;
|
||||||
if(!useAsFrontPage) {
|
|
||||||
setTitle(kioskTranslatedName);
|
setTitle(kioskTranslatedName);
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.getErrors().isEmpty()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
showSnackBarError(result.getErrors(),
|
showSnackBarError(result.getErrors(),
|
||||||
|
|
|
@ -2,11 +2,10 @@ package org.schabi.newpipe.fragments.list.playlist;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -29,17 +28,19 @@ 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.stream.StreamType;
|
||||||
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;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
|
||||||
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.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.ShareUtils;
|
||||||
|
import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -134,48 +135,40 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
infoListAdapter.useMiniItemVariants(true);
|
infoListAdapter.useMiniItemVariants(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueueStartingAt(StreamInfoItem infoItem) {
|
||||||
|
return getPlayQueue(Math.max(infoListAdapter.getItemsList().indexOf(infoItem), 0));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void showStreamDialog(final StreamInfoItem item) {
|
protected void showStreamDialog(StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
if (context == null || context.getResources() == null || activity == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
StreamDialogEntry.setEnabledEntries(
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
StreamDialogEntry.enqueue_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_main),
|
StreamDialogEntry.start_here_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_background),
|
StreamDialogEntry.append_playlist,
|
||||||
context.getResources().getString(R.string.start_here_on_popup),
|
StreamDialogEntry.share);
|
||||||
context.getResources().getString(R.string.share)
|
} else {
|
||||||
};
|
StreamDialogEntry.setEnabledEntries(
|
||||||
|
StreamDialogEntry.enqueue_on_background,
|
||||||
|
StreamDialogEntry.enqueue_on_popup,
|
||||||
|
StreamDialogEntry.start_here_on_background,
|
||||||
|
StreamDialogEntry.start_here_on_popup,
|
||||||
|
StreamDialogEntry.append_playlist,
|
||||||
|
StreamDialogEntry.share);
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
StreamDialogEntry.start_here_on_popup.setCustomAction(
|
||||||
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
(fragment, infoItem) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(infoItem), true));
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
shareUrl(item.getName(), item.getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
StreamDialogEntry.start_here_on_background.setCustomAction(
|
||||||
|
(fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(infoItem), true));
|
||||||
|
|
||||||
|
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||||
|
StreamDialogEntry.clickOn(which, this, item)).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -230,10 +223,10 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_item_openInBrowser:
|
case R.id.menu_item_openInBrowser:
|
||||||
openUrlInBrowser(url);
|
ShareUtils.openUrlInBrowser(this.getContext(), url);
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_share:
|
case R.id.menu_item_share:
|
||||||
shareUrl(name, url);
|
ShareUtils.shareUrl(this.getContext(), name, url);
|
||||||
break;
|
break;
|
||||||
case R.id.menu_item_bookmark:
|
case R.id.menu_item_bookmark:
|
||||||
onBookmarkClicked();
|
onBookmarkClicked();
|
||||||
|
@ -300,19 +293,19 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
.subscribe(getPlaylistBookmarkSubscriber());
|
.subscribe(getPlaylistBookmarkSubscriber());
|
||||||
|
|
||||||
headerPlayAllButton.setOnClickListener(view ->
|
headerPlayAllButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||||
headerPopupButton.setOnClickListener(view ->
|
headerPopupButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||||
headerBackgroundButton.setOnClickListener(view ->
|
headerBackgroundButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
|
|
||||||
headerPopupButton.setOnLongClickListener(view -> {
|
headerPopupButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue());
|
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
headerBackgroundButton.setOnLongClickListener(view -> {
|
headerBackgroundButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue());
|
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,13 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.support.v7.widget.TooltipCompat;
|
import androidx.appcompat.widget.TooltipCompat;
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
@ -73,7 +73,7 @@ import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import io.reactivex.subjects.PublishSubject;
|
import io.reactivex.subjects.PublishSubject;
|
||||||
|
|
||||||
import static android.support.v7.widget.helper.ItemTouchHelper.Callback.makeMovementFlags;
|
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ package org.schabi.newpipe.fragments.list.search;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.support.annotation.AttrRes;
|
import androidx.annotation.AttrRes;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
|
@ -4,8 +4,8 @@ import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@ -22,6 +21,7 @@ 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.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.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -59,13 +59,14 @@ public class InfoItemBuilder {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem) {
|
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
return buildView(parent, infoItem, false);
|
return buildView(parent, infoItem, historyRecordManager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem, boolean useMiniVariant) {
|
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem,
|
||||||
|
final HistoryRecordManager historyRecordManager, boolean useMiniVariant) {
|
||||||
InfoItemHolder holder = holderFromInfoType(parent, infoItem.getInfoType(), useMiniVariant);
|
InfoItemHolder holder = holderFromInfoType(parent, infoItem.getInfoType(), useMiniVariant);
|
||||||
holder.updateFromItem(infoItem);
|
holder.updateFromItem(infoItem, historyRecordManager);
|
||||||
return holder.itemView;
|
return holder.itemView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +81,6 @@ public class InfoItemBuilder {
|
||||||
case COMMENT:
|
case COMMENT:
|
||||||
return useMiniVariant ? new CommentsMiniInfoItemHolder(this, parent) : new CommentsInfoItemHolder(this, parent);
|
return useMiniVariant ? new CommentsMiniInfoItemHolder(this, parent) : new CommentsInfoItemHolder(this, parent);
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Trollolo");
|
|
||||||
throw new RuntimeException("InfoType not expected = " + infoType.name());
|
throw new RuntimeException("InfoType not expected = " + infoType.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package org.schabi.newpipe.info_list;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.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.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
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.ChannelGridInfoItemHolder;
|
||||||
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.CommentsInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
|
||||||
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
|
||||||
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.PlaylistGridInfoItemHolder;
|
||||||
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
||||||
|
@ -24,6 +27,7 @@ import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
|
||||||
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
|
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.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.FallbackViewHolder;
|
import org.schabi.newpipe.util.FallbackViewHolder;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
|
||||||
|
@ -71,6 +75,8 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
|
|
||||||
private final InfoItemBuilder infoItemBuilder;
|
private final InfoItemBuilder infoItemBuilder;
|
||||||
private final ArrayList<InfoItem> infoItemList;
|
private final ArrayList<InfoItem> infoItemList;
|
||||||
|
private final HistoryRecordManager recordManager;
|
||||||
|
|
||||||
private boolean useMiniVariant = false;
|
private boolean useMiniVariant = false;
|
||||||
private boolean useGridVariant = false;
|
private boolean useGridVariant = false;
|
||||||
private boolean showFooter = false;
|
private boolean showFooter = false;
|
||||||
|
@ -86,8 +92,9 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InfoListAdapter(Activity a) {
|
public InfoListAdapter(Context context) {
|
||||||
infoItemBuilder = new InfoItemBuilder(a);
|
this.recordManager = new HistoryRecordManager(context);
|
||||||
|
infoItemBuilder = new InfoItemBuilder(context);
|
||||||
infoItemList = new ArrayList<>();
|
infoItemList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,50 +122,53 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
this.useGridVariant = useGridVariant;
|
this.useGridVariant = useGridVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInfoItemList(List<InfoItem> data) {
|
public void addInfoItemList(@Nullable final List<InfoItem> data) {
|
||||||
if (data != null) {
|
if (data == null) {
|
||||||
if (DEBUG) {
|
return;
|
||||||
Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " + infoItemList.size() + ", data.size() = " + data.size());
|
|
||||||
}
|
}
|
||||||
|
if (DEBUG) Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " +
|
||||||
|
infoItemList.size() + ", data.size() = " + data.size());
|
||||||
|
|
||||||
int offsetStart = sizeConsideringHeaderOffset();
|
int offsetStart = sizeConsideringHeaderOffset();
|
||||||
infoItemList.addAll(data);
|
infoItemList.addAll(data);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) Log.d(TAG, "addInfoItemList() after > offsetStart = " + offsetStart +
|
||||||
Log.d(TAG, "addInfoItemList() after > offsetStart = " + offsetStart + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
", infoItemList.size() = " + infoItemList.size() +
|
||||||
}
|
", header = " + header + ", footer = " + footer +
|
||||||
|
", showFooter = " + showFooter);
|
||||||
notifyItemRangeInserted(offsetStart, data.size());
|
notifyItemRangeInserted(offsetStart, data.size());
|
||||||
|
|
||||||
if (footer != null && showFooter) {
|
if (footer != null && showFooter) {
|
||||||
int footerNow = sizeConsideringHeaderOffset();
|
int footerNow = sizeConsideringHeaderOffset();
|
||||||
notifyItemMoved(offsetStart, footerNow);
|
notifyItemMoved(offsetStart, footerNow);
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "addInfoItemList() footer from " + offsetStart + " to " + footerNow);
|
if (DEBUG) Log.d(TAG, "addInfoItemList() footer from " + offsetStart +
|
||||||
}
|
" to " + footerNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInfoItem(InfoItem data) {
|
public void addInfoItem(@Nullable InfoItem data) {
|
||||||
if (data != null) {
|
if (data == null) {
|
||||||
if (DEBUG) {
|
return;
|
||||||
Log.d(TAG, "addInfoItem() before > infoItemList.size() = " + infoItemList.size() + ", thread = " + Thread.currentThread());
|
|
||||||
}
|
}
|
||||||
|
if (DEBUG) Log.d(TAG, "addInfoItem() before > infoItemList.size() = " +
|
||||||
|
infoItemList.size() + ", thread = " + Thread.currentThread());
|
||||||
|
|
||||||
int positionInserted = sizeConsideringHeaderOffset();
|
int positionInserted = sizeConsideringHeaderOffset();
|
||||||
infoItemList.add(data);
|
infoItemList.add(data);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) Log.d(TAG, "addInfoItem() after > position = " + positionInserted +
|
||||||
Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
", infoItemList.size() = " + infoItemList.size() +
|
||||||
}
|
", header = " + header + ", footer = " + footer +
|
||||||
|
", showFooter = " + showFooter);
|
||||||
notifyItemInserted(positionInserted);
|
notifyItemInserted(positionInserted);
|
||||||
|
|
||||||
if (footer != null && showFooter) {
|
if (footer != null && showFooter) {
|
||||||
int footerNow = sizeConsideringHeaderOffset();
|
int footerNow = sizeConsideringHeaderOffset();
|
||||||
notifyItemMoved(positionInserted, footerNow);
|
notifyItemMoved(positionInserted, footerNow);
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "addInfoItem() footer from " + positionInserted + " to " + footerNow);
|
if (DEBUG) Log.d(TAG, "addInfoItem() footer from " + positionInserted +
|
||||||
}
|
" to " + footerNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,13 +245,13 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
case COMMENT:
|
case COMMENT:
|
||||||
return useMiniVariant ? MINI_COMMENT_HOLDER_TYPE : COMMENT_HOLDER_TYPE;
|
return useMiniVariant ? MINI_COMMENT_HOLDER_TYPE : COMMENT_HOLDER_TYPE;
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Trollolo");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
|
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -272,19 +282,18 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
case COMMENT_HOLDER_TYPE:
|
case COMMENT_HOLDER_TYPE:
|
||||||
return new CommentsInfoItemHolder(infoItemBuilder, parent);
|
return new CommentsInfoItemHolder(infoItemBuilder, parent);
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Trollolo");
|
|
||||||
return new FallbackViewHolder(new View(parent.getContext()));
|
return new FallbackViewHolder(new View(parent.getContext()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" + holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" + holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
||||||
if (holder instanceof InfoItemHolder) {
|
if (holder instanceof InfoItemHolder) {
|
||||||
// If header isn't null, offset the items by -1
|
// If header isn't null, offset the items by -1
|
||||||
if (header != null) position--;
|
if (header != null) position--;
|
||||||
|
|
||||||
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position));
|
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position), recordManager);
|
||||||
} else if (holder instanceof HFHolder && position == 0 && header != null) {
|
} else if (holder instanceof HFHolder && position == 0 && header != null) {
|
||||||
((HFHolder) holder).view = header;
|
((HFHolder) holder).view = header;
|
||||||
} else if (holder instanceof HFHolder && position == sizeConsideringHeaderOffset() && footer != null && showFooter) {
|
} else if (holder instanceof HFHolder && position == sizeConsideringHeaderOffset() && footer != null && showFooter) {
|
||||||
|
@ -292,6 +301,21 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
|
||||||
|
if (!payloads.isEmpty() && holder instanceof InfoItemHolder) {
|
||||||
|
for (Object payload : payloads) {
|
||||||
|
if (payload instanceof StreamStateEntity) {
|
||||||
|
((InfoItemHolder) holder).updateState(infoItemList.get(header == null ? position : position - 1), recordManager);
|
||||||
|
} else if (payload instanceof Boolean) {
|
||||||
|
((InfoItemHolder) holder).updateState(infoItemList.get(header == null ? position : position - 1), recordManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||||
return new GridLayoutManager.SpanSizeLookup() {
|
return new GridLayoutManager.SpanSizeLookup() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -38,8 +39,8 @@ public class ChannelInfoItemHolder extends ChannelMiniInfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
super.updateFromItem(infoItem);
|
super.updateFromItem(infoItem, historyRecordManager);
|
||||||
|
|
||||||
if (!(infoItem instanceof ChannelInfoItem)) return;
|
if (!(infoItem instanceof ChannelInfoItem)) return;
|
||||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
if (!(infoItem instanceof ChannelInfoItem)) return;
|
if (!(infoItem instanceof ChannelInfoItem)) return;
|
||||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 12.02.17.
|
* Created by Christian Schabesberger on 12.02.17.
|
||||||
|
@ -41,8 +40,8 @@ public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
super.updateFromItem(infoItem);
|
super.updateFromItem(infoItem, historyRecordManager);
|
||||||
|
|
||||||
if (!(infoItem instanceof CommentsInfoItem)) return;
|
if (!(infoItem instanceof CommentsInfoItem)) return;
|
||||||
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
package org.schabi.newpipe.info_list.holder;
|
package org.schabi.newpipe.info_list.holder;
|
||||||
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.jsoup.helper.StringUtil;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.util.CommentTextOnTouchListener;
|
import org.schabi.newpipe.util.CommentTextOnTouchListener;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
|
@ -45,7 +46,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
if(hours != null) timestamp += (Integer.parseInt(hours.replace(":", ""))*3600);
|
if(hours != null) timestamp += (Integer.parseInt(hours.replace(":", ""))*3600);
|
||||||
if(minutes != null) timestamp += (Integer.parseInt(minutes.replace(":", ""))*60);
|
if(minutes != null) timestamp += (Integer.parseInt(minutes.replace(":", ""))*60);
|
||||||
if(seconds != null) timestamp += (Integer.parseInt(seconds));
|
if(seconds != null) timestamp += (Integer.parseInt(seconds));
|
||||||
return streamUrl + url.replace(match.group(0), "&t=" + String.valueOf(timestamp));
|
return streamUrl + url.replace(match.group(0), "#timestamp=" + timestamp);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
if (!(infoItem instanceof CommentsInfoItem)) return;
|
if (!(infoItem instanceof CommentsInfoItem)) return;
|
||||||
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
||||||
|
|
||||||
|
@ -73,9 +74,8 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
itemThumbnailView,
|
itemThumbnailView,
|
||||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS);
|
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS);
|
||||||
|
|
||||||
itemThumbnailView.setOnClickListener(new View.OnClickListener() {
|
itemThumbnailView.setOnClickListener(view -> {
|
||||||
@Override
|
if(StringUtil.isBlank(item.getAuthorEndpoint())) return;
|
||||||
public void onClick(View view) {
|
|
||||||
try {
|
try {
|
||||||
final AppCompatActivity activity = (AppCompatActivity) itemBuilder.getContext();
|
final AppCompatActivity activity = (AppCompatActivity) itemBuilder.getContext();
|
||||||
NavigationHelper.openChannelFragment(
|
NavigationHelper.openChannelFragment(
|
||||||
|
@ -86,19 +86,17 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorActivity.reportUiError((AppCompatActivity) itemBuilder.getContext(), e);
|
ErrorActivity.reportUiError((AppCompatActivity) itemBuilder.getContext(), e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
streamUrl = item.getUrl();
|
streamUrl = item.getUrl();
|
||||||
|
|
||||||
itemContentView.setMaxLines(commentDefaultLines);
|
itemContentView.setLines(commentDefaultLines);
|
||||||
commentText = item.getCommentText();
|
commentText = item.getCommentText();
|
||||||
itemContentView.setText(commentText);
|
itemContentView.setText(commentText);
|
||||||
linkify();
|
|
||||||
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
|
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
|
||||||
|
|
||||||
if (itemContentView.getLineCount() == 0) {
|
if (itemContentView.getLineCount() == 0) {
|
||||||
itemContentView.post(() -> ellipsize());
|
itemContentView.post(this::ellipsize);
|
||||||
} else {
|
} else {
|
||||||
ellipsize();
|
ellipsize();
|
||||||
}
|
}
|
||||||
|
@ -119,15 +117,17 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
||||||
private void ellipsize() {
|
private void ellipsize() {
|
||||||
if (itemContentView.getLineCount() > commentDefaultLines){
|
if (itemContentView.getLineCount() > commentDefaultLines){
|
||||||
int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1);
|
int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1);
|
||||||
String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "...";
|
int end = itemContentView.getText().toString().lastIndexOf(' ', endOfLastLine -2);
|
||||||
|
if(end == -1) end = Math.max(endOfLastLine -2, 0);
|
||||||
|
String newVal = itemContentView.getText().subSequence(0, end) + " …";
|
||||||
itemContentView.setText(newVal);
|
itemContentView.setText(newVal);
|
||||||
linkify();
|
|
||||||
}
|
}
|
||||||
|
linkify();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleEllipsize() {
|
private void toggleEllipsize() {
|
||||||
if (itemContentView.getText().toString().equals(commentText)) {
|
if (itemContentView.getText().toString().equals(commentText)) {
|
||||||
ellipsize();
|
if (itemContentView.getLineCount() > commentDefaultLines) ellipsize();
|
||||||
} else {
|
} else {
|
||||||
expand();
|
expand();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package org.schabi.newpipe.info_list.holder;
|
package org.schabi.newpipe.info_list.holder;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 12.02.17.
|
* Created by Christian Schabesberger on 12.02.17.
|
||||||
|
@ -35,5 +36,8 @@ public abstract class InfoItemHolder extends RecyclerView.ViewHolder {
|
||||||
this.itemBuilder = infoItemBuilder;
|
this.itemBuilder = infoItemBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void updateFromItem(final InfoItem infoItem);
|
public abstract void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager);
|
||||||
|
|
||||||
|
public void updateState(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
|
|
||||||
public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
|
public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
|
||||||
|
@ -30,7 +31,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
||||||
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -40,8 +41,8 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
super.updateFromItem(infoItem);
|
super.updateFromItem(infoItem, historyRecordManager);
|
||||||
|
|
||||||
if (!(infoItem instanceof StreamInfoItem)) return;
|
if (!(infoItem instanceof StreamInfoItem)) return;
|
||||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
package org.schabi.newpipe.info_list.holder;
|
package org.schabi.newpipe.info_list.holder;
|
||||||
|
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
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.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
|
|
||||||
|
@ -20,6 +26,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
public final TextView itemVideoTitleView;
|
public final TextView itemVideoTitleView;
|
||||||
public final TextView itemUploaderView;
|
public final TextView itemUploaderView;
|
||||||
public final TextView itemDurationView;
|
public final TextView itemDurationView;
|
||||||
|
public final AnimatedProgressBar itemProgressView;
|
||||||
|
|
||||||
StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||||
super(infoItemBuilder, layoutId, parent);
|
super(infoItemBuilder, layoutId, parent);
|
||||||
|
@ -28,6 +35,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView);
|
itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView);
|
||||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||||
|
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
public StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||||
|
@ -35,7 +43,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final InfoItem infoItem) {
|
public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
if (!(infoItem instanceof StreamInfoItem)) return;
|
if (!(infoItem instanceof StreamInfoItem)) return;
|
||||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||||
|
|
||||||
|
@ -47,13 +55,24 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||||
R.color.duration_background_color));
|
R.color.duration_background_color));
|
||||||
itemDurationView.setVisibility(View.VISIBLE);
|
itemDurationView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
StreamStateEntity state2 = historyRecordManager.loadStreamState(infoItem).blockingGet()[0];
|
||||||
|
if (state2 != null) {
|
||||||
|
itemProgressView.setVisibility(View.VISIBLE);
|
||||||
|
itemProgressView.setMax((int) item.getDuration());
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state2.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
} else if (item.getStreamType() == StreamType.LIVE_STREAM) {
|
} else if (item.getStreamType() == StreamType.LIVE_STREAM) {
|
||||||
itemDurationView.setText(R.string.duration_live);
|
itemDurationView.setText(R.string.duration_live);
|
||||||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||||
R.color.live_duration_background_color));
|
R.color.live_duration_background_color));
|
||||||
itemDurationView.setVisibility(View.VISIBLE);
|
itemDurationView.setVisibility(View.VISIBLE);
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
itemDurationView.setVisibility(View.GONE);
|
itemDurationView.setVisibility(View.GONE);
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -83,6 +102,24 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) {
|
||||||
|
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||||
|
|
||||||
|
StreamStateEntity state = historyRecordManager.loadStreamState(infoItem).blockingGet()[0];
|
||||||
|
if (state != null && item.getDuration() > 0 && item.getStreamType() != StreamType.LIVE_STREAM) {
|
||||||
|
itemProgressView.setMax((int) item.getDuration());
|
||||||
|
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||||
|
}
|
||||||
|
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void enableLongClick(final StreamInfoItem item) {
|
private void enableLongClick(final StreamInfoItem item) {
|
||||||
itemView.setLongClickable(true);
|
itemView.setLongClickable(true);
|
||||||
itemView.setOnLongClickListener(view -> {
|
itemView.setOnLongClickListener(view -> {
|
||||||
|
|
|
@ -5,11 +5,11 @@ import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.local;
|
package org.schabi.newpipe.local;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
public class HeaderFooterHolder extends RecyclerView.ViewHolder {
|
public class HeaderFooterHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package org.schabi.newpipe.local;
|
package org.schabi.newpipe.local;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.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.database.stream.model.StreamStateEntity;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
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.LocalPlaylistGridItemHolder;
|
||||||
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
||||||
|
@ -64,6 +68,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
|
|
||||||
private final LocalItemBuilder localItemBuilder;
|
private final LocalItemBuilder localItemBuilder;
|
||||||
private final ArrayList<LocalItem> localItems;
|
private final ArrayList<LocalItem> localItems;
|
||||||
|
private final HistoryRecordManager recordManager;
|
||||||
private final DateFormat dateFormat;
|
private final DateFormat dateFormat;
|
||||||
|
|
||||||
private boolean showFooter = false;
|
private boolean showFooter = false;
|
||||||
|
@ -71,11 +76,12 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
private View header = null;
|
private View header = null;
|
||||||
private View footer = null;
|
private View footer = null;
|
||||||
|
|
||||||
public LocalItemListAdapter(Activity activity) {
|
public LocalItemListAdapter(Context context) {
|
||||||
localItemBuilder = new LocalItemBuilder(activity);
|
recordManager = new HistoryRecordManager(context);
|
||||||
|
localItemBuilder = new LocalItemBuilder(context);
|
||||||
localItems = new ArrayList<>();
|
localItems = new ArrayList<>();
|
||||||
dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
|
dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
|
||||||
Localization.getPreferredLocale(activity));
|
Localization.getPreferredLocale(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedListener(OnClickGesture<LocalItem> listener) {
|
public void setSelectedListener(OnClickGesture<LocalItem> listener) {
|
||||||
|
@ -86,23 +92,20 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
localItemBuilder.setOnItemSelectedListener(null);
|
localItemBuilder.setOnItemSelectedListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItems(List<? extends LocalItem> data) {
|
public void addItems(@Nullable List<? extends LocalItem> data) {
|
||||||
if (data != null) {
|
if (data == null) {
|
||||||
if (DEBUG) {
|
return;
|
||||||
Log.d(TAG, "addItems() before > localItems.size() = " +
|
|
||||||
localItems.size() + ", data.size() = " + data.size());
|
|
||||||
}
|
}
|
||||||
|
if (DEBUG) Log.d(TAG, "addItems() before > localItems.size() = " +
|
||||||
|
localItems.size() + ", data.size() = " + data.size());
|
||||||
|
|
||||||
int offsetStart = sizeConsideringHeader();
|
int offsetStart = sizeConsideringHeader();
|
||||||
localItems.addAll(data);
|
localItems.addAll(data);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
||||||
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
|
||||||
", localItems.size() = " + localItems.size() +
|
", localItems.size() = " + localItems.size() +
|
||||||
", header = " + header + ", footer = " + footer +
|
", header = " + header + ", footer = " + footer +
|
||||||
", showFooter = " + showFooter);
|
", showFooter = " + showFooter);
|
||||||
}
|
|
||||||
|
|
||||||
notifyItemRangeInserted(offsetStart, data.size());
|
notifyItemRangeInserted(offsetStart, data.size());
|
||||||
|
|
||||||
if (footer != null && showFooter) {
|
if (footer != null && showFooter) {
|
||||||
|
@ -113,11 +116,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
" to " + footerNow);
|
" to " + footerNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void removeItem(final LocalItem data) {
|
public void removeItem(final LocalItem data) {
|
||||||
final int index = localItems.indexOf(data);
|
final int index = localItems.indexOf(data);
|
||||||
|
|
||||||
localItems.remove(index);
|
localItems.remove(index);
|
||||||
notifyItemRemoved(index + (header != null ? 1 : 0));
|
notifyItemRemoved(index + (header != null ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
@ -219,8 +220,9 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
|
||||||
if (DEBUG) Log.d(TAG, "onCreateViewHolder() called with: parent = [" +
|
if (DEBUG) Log.d(TAG, "onCreateViewHolder() called with: parent = [" +
|
||||||
parent + "], type = [" + type + "]");
|
parent + "], type = [" + type + "]");
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -251,7 +253,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" +
|
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" +
|
||||||
holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
||||||
|
|
||||||
|
@ -259,7 +261,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
// If header isn't null, offset the items by -1
|
// If header isn't null, offset the items by -1
|
||||||
if (header != null) position--;
|
if (header != null) position--;
|
||||||
|
|
||||||
((LocalItemHolder) holder).updateFromItem(localItems.get(position), dateFormat);
|
((LocalItemHolder) holder).updateFromItem(localItems.get(position), recordManager, dateFormat);
|
||||||
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
|
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
|
||||||
((HeaderFooterHolder) holder).view = header;
|
((HeaderFooterHolder) holder).view = header;
|
||||||
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
|
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
|
||||||
|
@ -268,6 +270,21 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
|
||||||
|
if (!payloads.isEmpty() && holder instanceof LocalItemHolder) {
|
||||||
|
for (Object payload : payloads) {
|
||||||
|
if (payload instanceof StreamStateEntity) {
|
||||||
|
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1), recordManager);
|
||||||
|
} else if (payload instanceof Boolean) {
|
||||||
|
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1), recordManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||||
return new GridLayoutManager.SpanSizeLookup() {
|
return new GridLayoutManager.SpanSizeLookup() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,9 +3,9 @@ package org.schabi.newpipe.local.bookmark;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package org.schabi.newpipe.local.dialog;
|
package org.schabi.newpipe.local.dialog;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -28,7 +27,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
|
||||||
public final class PlaylistAppendDialog extends PlaylistDialog {
|
public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
||||||
|
@ -36,7 +35,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
private RecyclerView playlistRecyclerView;
|
private RecyclerView playlistRecyclerView;
|
||||||
private LocalItemListAdapter playlistAdapter;
|
private LocalItemListAdapter playlistAdapter;
|
||||||
|
|
||||||
private Disposable playlistReactor;
|
private CompositeDisposable playlistDisposables = new CompositeDisposable();
|
||||||
|
|
||||||
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
|
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
|
||||||
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
||||||
|
@ -99,9 +98,9 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||||
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
||||||
|
|
||||||
playlistReactor = playlistManager.getPlaylists()
|
playlistDisposables.add(playlistManager.getPlaylists()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::onPlaylistsReceived);
|
.subscribe(this::onPlaylistsReceived));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -111,10 +110,12 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
if (playlistReactor != null) playlistReactor.dispose();
|
playlistDisposables.dispose();
|
||||||
if (playlistAdapter != null) playlistAdapter.unsetSelectedListener();
|
if (playlistAdapter != null) {
|
||||||
|
playlistAdapter.unsetSelectedListener();
|
||||||
|
}
|
||||||
|
|
||||||
playlistReactor = null;
|
playlistDisposables.clear();
|
||||||
playlistRecyclerView = null;
|
playlistRecyclerView = null;
|
||||||
playlistAdapter = null;
|
playlistAdapter = null;
|
||||||
}
|
}
|
||||||
|
@ -148,13 +149,12 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
@NonNull List<StreamEntity> streams) {
|
@NonNull List<StreamEntity> streams) {
|
||||||
if (getStreams() == null) return;
|
if (getStreams() == null) return;
|
||||||
|
|
||||||
@SuppressLint("ShowToast")
|
|
||||||
final Toast successToast = Toast.makeText(getContext(),
|
final Toast successToast = Toast.makeText(getContext(),
|
||||||
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
|
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
|
||||||
|
|
||||||
manager.appendToPlaylist(playlist.uid, streams)
|
playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(ignored -> successToast.show());
|
.subscribe(ignored -> successToast.show()));
|
||||||
|
|
||||||
getDialog().dismiss();
|
getDialog().dismiss();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package org.schabi.newpipe.local.dialog;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
|
@ -2,9 +2,9 @@ package org.schabi.newpipe.local.dialog;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
|
|
|
@ -2,9 +2,9 @@ package org.schabi.newpipe.local.feed;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -21,8 +21,8 @@ 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.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
import org.schabi.newpipe.fragments.list.BaseListFragment;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -183,7 +183,7 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleResult(@android.support.annotation.NonNull List<SubscriptionEntity> result) {
|
public void handleResult(@androidx.annotation.NonNull List<SubscriptionEntity> result) {
|
||||||
super.handleResult(result);
|
super.handleResult(result);
|
||||||
|
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
|
@ -262,7 +262,7 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
|
||||||
* If chosen feed already displayed, then we request another feed from another
|
* If chosen feed already displayed, then we request another feed from another
|
||||||
* subscription, until the subscription table runs out of new items.
|
* subscription, until the subscription table runs out of new items.
|
||||||
* <p>
|
* <p>
|
||||||
* This Observer is self-contained and will dispose itself when complete. However, this
|
* This Observer is self-contained and will close itself when complete. However, this
|
||||||
* does not obey the fragment lifecycle and may continue running in the background
|
* does not obey the fragment lifecycle and may continue running in the background
|
||||||
* until it is complete. This is done due to RxJava2 no longer propagate errors once
|
* until it is complete. This is done due to RxJava2 no longer propagate errors once
|
||||||
* an observer is unsubscribed while the thread process is still running.
|
* an observer is unsubscribed while the thread process is still running.
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.schabi.newpipe.local.history;
|
package org.schabi.newpipe.local.history;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.local.history;
|
package org.schabi.newpipe.local.history;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
|
|
@ -21,28 +21,34 @@ package org.schabi.newpipe.local.history;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
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.AppDatabase;
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
||||||
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
||||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
||||||
|
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||||
|
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||||
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
||||||
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Flowable;
|
import io.reactivex.Flowable;
|
||||||
import io.reactivex.Maybe;
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
@ -80,9 +86,9 @@ public class HistoryRecordManager {
|
||||||
final Date currentTime = new Date();
|
final Date currentTime = new Date();
|
||||||
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
|
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
|
||||||
final long streamId = streamTable.upsert(new StreamEntity(info));
|
final long streamId = streamTable.upsert(new StreamEntity(info));
|
||||||
StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry();
|
StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId);
|
||||||
|
|
||||||
if (latestEntry != null && latestEntry.getStreamUid() == streamId) {
|
if (latestEntry != null) {
|
||||||
streamHistoryTable.delete(latestEntry);
|
streamHistoryTable.delete(latestEntry);
|
||||||
latestEntry.setAccessDate(currentTime);
|
latestEntry.setAccessDate(currentTime);
|
||||||
latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1);
|
latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1);
|
||||||
|
@ -99,7 +105,12 @@ public class HistoryRecordManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> deleteWholeStreamHistory() {
|
public Single<Integer> deleteWholeStreamHistory() {
|
||||||
return Single.fromCallable(() -> streamHistoryTable.deleteAll())
|
return Single.fromCallable(streamHistoryTable::deleteAll)
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Single<Integer> deleteCompelteStreamStateHistory() {
|
||||||
|
return Single.fromCallable(streamStateTable::deleteAll)
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,8 +170,8 @@ public class HistoryRecordManager {
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> deleteWholeSearchHistory() {
|
public Single<Integer> deleteCompleteSearchHistory() {
|
||||||
return Single.fromCallable(() -> searchHistoryTable.deleteAll())
|
return Single.fromCallable(searchHistoryTable::deleteAll)
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,21 +191,104 @@ public class HistoryRecordManager {
|
||||||
// Stream State History
|
// Stream State History
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
public Maybe<StreamHistoryEntity> getStreamHistory(final StreamInfo info) {
|
||||||
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
|
return Maybe.fromCallable(() -> {
|
||||||
return Maybe.fromCallable(() -> streamTable.upsert(new StreamEntity(info)))
|
final long streamId = streamTable.upsert(new StreamEntity(info));
|
||||||
.flatMap(streamId -> streamStateTable.getState(streamId).firstElement())
|
return streamHistoryTable.getLatestEntry(streamId);
|
||||||
.flatMap(states -> states.isEmpty() ? Maybe.empty() : Maybe.just(states.get(0)))
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Maybe<StreamStateEntity> loadStreamState(final PlayQueueItem queueItem) {
|
||||||
|
return queueItem.getStream()
|
||||||
|
.map((info) -> streamTable.upsert(new StreamEntity(info)))
|
||||||
|
.flatMapPublisher(streamStateTable::getState)
|
||||||
|
.firstElement()
|
||||||
|
.flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0)))
|
||||||
|
.filter(state -> state.isValid((int) queueItem.getDuration()))
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Maybe<Long> saveStreamState(@NonNull final StreamInfo info, final long progressTime) {
|
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
|
||||||
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
|
return Single.fromCallable(() -> streamTable.upsert(new StreamEntity(info)))
|
||||||
|
.flatMapPublisher(streamStateTable::getState)
|
||||||
|
.firstElement()
|
||||||
|
.flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0)))
|
||||||
|
.filter(state -> state.isValid((int) info.getDuration()))
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable saveStreamState(@NonNull final StreamInfo info, final long progressTime) {
|
||||||
|
return Completable.fromAction(() -> database.runInTransaction(() -> {
|
||||||
final long streamId = streamTable.upsert(new StreamEntity(info));
|
final long streamId = streamTable.upsert(new StreamEntity(info));
|
||||||
return streamStateTable.upsert(new StreamStateEntity(streamId, progressTime));
|
final StreamStateEntity state = new StreamStateEntity(streamId, progressTime);
|
||||||
|
if (state.isValid((int) info.getDuration())) {
|
||||||
|
streamStateTable.upsert(state);
|
||||||
|
} else {
|
||||||
|
streamStateTable.deleteState(streamId);
|
||||||
|
}
|
||||||
})).subscribeOn(Schedulers.io());
|
})).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Single<StreamStateEntity[]> loadStreamState(final InfoItem info) {
|
||||||
|
return Single.fromCallable(() -> {
|
||||||
|
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
|
||||||
|
if (entities.isEmpty()) {
|
||||||
|
return new StreamStateEntity[]{null};
|
||||||
|
}
|
||||||
|
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
|
||||||
|
if (states.isEmpty()) {
|
||||||
|
return new StreamStateEntity[]{null};
|
||||||
|
}
|
||||||
|
return new StreamStateEntity[]{states.get(0)};
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Single<List<StreamStateEntity>> loadStreamStateBatch(final List<InfoItem> infos) {
|
||||||
|
return Single.fromCallable(() -> {
|
||||||
|
final List<StreamStateEntity> result = new ArrayList<>(infos.size());
|
||||||
|
for (InfoItem info : infos) {
|
||||||
|
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
|
||||||
|
if (entities.isEmpty()) {
|
||||||
|
result.add(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
|
||||||
|
if (states.isEmpty()) {
|
||||||
|
result.add(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.add(states.get(0));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Single<List<StreamStateEntity>> loadLocalStreamStateBatch(final List<? extends LocalItem> items) {
|
||||||
|
return Single.fromCallable(() -> {
|
||||||
|
final List<StreamStateEntity> result = new ArrayList<>(items.size());
|
||||||
|
for (LocalItem item : items) {
|
||||||
|
long streamId;
|
||||||
|
if (item instanceof StreamStatisticsEntry) {
|
||||||
|
streamId = ((StreamStatisticsEntry) item).streamId;
|
||||||
|
} else if (item instanceof PlaylistStreamEntity) {
|
||||||
|
streamId = ((PlaylistStreamEntity) item).getStreamUid();
|
||||||
|
} else if (item instanceof PlaylistStreamEntry) {
|
||||||
|
streamId = ((PlaylistStreamEntry) item).streamId;
|
||||||
|
} else {
|
||||||
|
result.add(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final List<StreamStateEntity> states = streamStateTable.getState(streamId).blockingFirst();
|
||||||
|
if (states.isEmpty()) {
|
||||||
|
result.add(null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.add(states.get(0));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// Utility
|
// Utility
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
@ -202,4 +296,5 @@ public class HistoryRecordManager {
|
||||||
public Single<Integer> removeOrphanedRecords() {
|
public Single<Integer> removeOrphanedRecords() {
|
||||||
return Single.fromCallable(streamTable::deleteOrphans).subscribeOn(Schedulers.io());
|
return Single.fromCallable(streamTable::deleteOrphans).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,12 @@ package org.schabi.newpipe.local.history;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.design.widget.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -25,6 +24,7 @@ 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.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
|
@ -34,6 +34,7 @@ import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
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.StreamDialogEntry;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -179,7 +180,7 @@ public class StatisticsPlaylistFragment
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(getContext(),
|
howManyDeleted -> Toast.makeText(getContext(),
|
||||||
R.string.view_history_deleted,
|
R.string.watch_history_deleted,
|
||||||
Toast.LENGTH_SHORT).show(),
|
Toast.LENGTH_SHORT).show(),
|
||||||
throwable -> ErrorActivity.reportError(getContext(),
|
throwable -> ErrorActivity.reportError(getContext(),
|
||||||
throwable,
|
throwable,
|
||||||
|
@ -309,11 +310,11 @@ public class StatisticsPlaylistFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
headerPlayAllButton.setOnClickListener(view ->
|
headerPlayAllButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||||
headerPopupButton.setOnClickListener(view ->
|
headerPopupButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||||
headerBackgroundButton.setOnClickListener(view ->
|
headerBackgroundButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
sortButton.setOnClickListener(view -> toggleSortMode());
|
sortButton.setOnClickListener(view -> toggleSortMode());
|
||||||
|
|
||||||
hideLoading();
|
hideLoading();
|
||||||
|
@ -356,52 +357,44 @@ public class StatisticsPlaylistFragment
|
||||||
startLoading(true);
|
startLoading(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueueStartingAt(StreamStatisticsEntry infoItem) {
|
||||||
|
return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0));
|
||||||
|
}
|
||||||
|
|
||||||
private void showStreamDialog(final StreamStatisticsEntry item) {
|
private void showStreamDialog(final StreamStatisticsEntry item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
if (context == null || context.getResources() == null || activity == null) return;
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
StreamDialogEntry.setEnabledEntries(
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
StreamDialogEntry.enqueue_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_main),
|
StreamDialogEntry.start_here_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_background),
|
StreamDialogEntry.delete,
|
||||||
context.getResources().getString(R.string.start_here_on_popup),
|
StreamDialogEntry.append_playlist,
|
||||||
context.getResources().getString(R.string.delete),
|
StreamDialogEntry.share);
|
||||||
context.getResources().getString(R.string.share)
|
} else {
|
||||||
};
|
StreamDialogEntry.setEnabledEntries(
|
||||||
|
StreamDialogEntry.enqueue_on_background,
|
||||||
|
StreamDialogEntry.enqueue_on_popup,
|
||||||
|
StreamDialogEntry.start_here_on_background,
|
||||||
|
StreamDialogEntry.start_here_on_popup,
|
||||||
|
StreamDialogEntry.delete,
|
||||||
|
StreamDialogEntry.append_playlist,
|
||||||
|
StreamDialogEntry.share);
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
StreamDialogEntry.start_here_on_popup.setCustomAction(
|
||||||
final int index = Math.max(itemListAdapter.getItemsList().indexOf(item), 0);
|
(fragment, infoItemDuplicate) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem));
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(infoItem));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
deleteEntry(index);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), infoItem, commands, actions).show();
|
StreamDialogEntry.start_here_on_background.setCustomAction(
|
||||||
|
(fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
|
||||||
|
StreamDialogEntry.delete.setCustomAction((fragment, infoItemDuplicate) ->
|
||||||
|
deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0)));
|
||||||
|
|
||||||
|
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||||
|
StreamDialogEntry.clickOn(which, this, infoItem)).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteEntry(final int index) {
|
private void deleteEntry(final int index) {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package org.schabi.newpipe.local.holder;
|
package org.schabi.newpipe.local.holder;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
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.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
|
||||||
|
@ -38,5 +39,8 @@ public abstract class LocalItemHolder extends RecyclerView.ViewHolder {
|
||||||
this.itemBuilder = itemBuilder;
|
this.itemBuilder = itemBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void updateFromItem(final LocalItem item, final DateFormat dateFormat);
|
public abstract void updateFromItem(final LocalItem item, HistoryRecordManager historyRecordManager, final DateFormat dateFormat);
|
||||||
|
|
||||||
|
public void updateState(final LocalItem localItem, HistoryRecordManager historyRecordManager) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.view.ViewGroup;
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
@ -21,7 +22,7 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
|
||||||
if (!(localItem instanceof PlaylistMetadataEntry)) return;
|
if (!(localItem instanceof PlaylistMetadataEntry)) return;
|
||||||
final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem;
|
final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem;
|
||||||
|
|
||||||
|
@ -32,6 +33,6 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
||||||
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView,
|
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView,
|
||||||
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
||||||
|
|
||||||
super.updateFromItem(localItem, dateFormat);
|
super.updateFromItem(localItem, historyRecordManager, dateFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.local.holder;
|
package org.schabi.newpipe.local.holder;
|
||||||
|
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -10,12 +10,18 @@ import android.widget.TextView;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||||
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
|
|
||||||
|
@ -24,6 +30,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
public final TextView itemAdditionalDetailsView;
|
public final TextView itemAdditionalDetailsView;
|
||||||
public final TextView itemDurationView;
|
public final TextView itemDurationView;
|
||||||
public final View itemHandleView;
|
public final View itemHandleView;
|
||||||
|
public final AnimatedProgressBar itemProgressView;
|
||||||
|
|
||||||
LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||||
super(infoItemBuilder, layoutId, parent);
|
super(infoItemBuilder, layoutId, parent);
|
||||||
|
@ -33,6 +40,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
itemAdditionalDetailsView = itemView.findViewById(R.id.itemAdditionalDetails);
|
itemAdditionalDetailsView = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||||
itemHandleView = itemView.findViewById(R.id.itemHandle);
|
itemHandleView = itemView.findViewById(R.id.itemHandle);
|
||||||
|
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
public LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||||
|
@ -40,7 +48,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
|
||||||
if (!(localItem instanceof PlaylistStreamEntry)) return;
|
if (!(localItem instanceof PlaylistStreamEntry)) return;
|
||||||
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
|
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
|
||||||
|
|
||||||
|
@ -53,6 +61,15 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||||
R.color.duration_background_color));
|
R.color.duration_background_color));
|
||||||
itemDurationView.setVisibility(View.VISIBLE);
|
itemDurationView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
|
||||||
|
if (state != null) {
|
||||||
|
itemProgressView.setVisibility(View.VISIBLE);
|
||||||
|
itemProgressView.setMax((int) item.duration);
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
itemDurationView.setVisibility(View.GONE);
|
itemDurationView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
@ -79,6 +96,25 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
itemHandleView.setOnTouchListener(getOnTouchListener(item));
|
itemHandleView.setOnTouchListener(getOnTouchListener(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(LocalItem localItem, HistoryRecordManager historyRecordManager) {
|
||||||
|
if (!(localItem instanceof PlaylistStreamEntry)) return;
|
||||||
|
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
|
||||||
|
|
||||||
|
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
|
||||||
|
if (state != null && item.duration > 0) {
|
||||||
|
itemProgressView.setMax((int) item.duration);
|
||||||
|
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||||
|
}
|
||||||
|
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private View.OnTouchListener getOnTouchListener(final PlaylistStreamEntry item) {
|
private View.OnTouchListener getOnTouchListener(final PlaylistStreamEntry item) {
|
||||||
return (view, motionEvent) -> {
|
return (view, motionEvent) -> {
|
||||||
view.performClick();
|
view.performClick();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.schabi.newpipe.local.holder;
|
package org.schabi.newpipe.local.holder;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
@ -10,12 +10,18 @@ import android.widget.TextView;
|
||||||
import org.schabi.newpipe.R;
|
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.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
|
@ -45,6 +51,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
||||||
public final TextView itemDurationView;
|
public final TextView itemDurationView;
|
||||||
@Nullable
|
@Nullable
|
||||||
public final TextView itemAdditionalDetails;
|
public final TextView itemAdditionalDetails;
|
||||||
|
public final AnimatedProgressBar itemProgressView;
|
||||||
|
|
||||||
public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
|
public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
|
||||||
this(itemBuilder, R.layout.list_stream_item, parent);
|
this(itemBuilder, R.layout.list_stream_item, parent);
|
||||||
|
@ -58,6 +65,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
||||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||||
itemAdditionalDetails = itemView.findViewById(R.id.itemAdditionalDetails);
|
itemAdditionalDetails = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||||
|
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getStreamInfoDetailLine(final StreamStatisticsEntry entry,
|
private String getStreamInfoDetailLine(final StreamStatisticsEntry entry,
|
||||||
|
@ -70,7 +78,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
|
||||||
if (!(localItem instanceof StreamStatisticsEntry)) return;
|
if (!(localItem instanceof StreamStatisticsEntry)) return;
|
||||||
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
|
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
|
||||||
|
|
||||||
|
@ -82,8 +90,18 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
||||||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||||
R.color.duration_background_color));
|
R.color.duration_background_color));
|
||||||
itemDurationView.setVisibility(View.VISIBLE);
|
itemDurationView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
|
||||||
|
if (state != null) {
|
||||||
|
itemProgressView.setVisibility(View.VISIBLE);
|
||||||
|
itemProgressView.setMax((int) item.duration);
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
itemDurationView.setVisibility(View.GONE);
|
itemDurationView.setVisibility(View.GONE);
|
||||||
|
itemProgressView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemAdditionalDetails != null) {
|
if (itemAdditionalDetails != null) {
|
||||||
|
@ -108,4 +126,23 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(LocalItem localItem, HistoryRecordManager historyRecordManager) {
|
||||||
|
if (!(localItem instanceof StreamStatisticsEntry)) return;
|
||||||
|
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
|
||||||
|
|
||||||
|
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
|
||||||
|
if (state != null && item.duration > 0) {
|
||||||
|
itemProgressView.setMax((int) item.duration);
|
||||||
|
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
} else {
|
||||||
|
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||||
|
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||||
|
}
|
||||||
|
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||||
|
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.widget.TextView;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ public abstract class PlaylistItemHolder extends LocalItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
|
||||||
itemView.setOnClickListener(view -> {
|
itemView.setOnClickListener(view -> {
|
||||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||||
itemBuilder.getOnItemSelectedListener().selected(localItem);
|
itemBuilder.getOnItemSelectedListener().selected(localItem);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||||
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
|
||||||
if (!(localItem instanceof PlaylistRemoteEntity)) return;
|
if (!(localItem instanceof PlaylistRemoteEntity)) return;
|
||||||
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
|
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
|
||||||
|
|
||||||
|
@ -33,6 +34,6 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
|
||||||
itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView,
|
itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView,
|
||||||
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
||||||
|
|
||||||
super.updateFromItem(localItem, dateFormat);
|
super.updateFromItem(localItem, historyRecordManager, dateFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ package org.schabi.newpipe.local.playlist;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v7.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -26,14 +25,16 @@ import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
|
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||||
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.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.Localization;
|
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.StreamDialogEntry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -318,11 +319,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
setVideoCount(itemListAdapter.getItemsList().size());
|
setVideoCount(itemListAdapter.getItemsList().size());
|
||||||
|
|
||||||
headerPlayAllButton.setOnClickListener(view ->
|
headerPlayAllButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||||
headerPopupButton.setOnClickListener(view ->
|
headerPopupButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||||
headerBackgroundButton.setOnClickListener(view ->
|
headerBackgroundButton.setOnClickListener(view ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
|
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
|
@ -510,59 +511,48 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueueStartingAt(PlaylistStreamEntry infoItem) {
|
||||||
|
return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0));
|
||||||
|
}
|
||||||
|
|
||||||
protected void showStreamItemDialog(final PlaylistStreamEntry item) {
|
protected void showStreamItemDialog(final PlaylistStreamEntry item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
if (context == null || context.getResources() == null || activity == null) return;
|
||||||
|
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
context.getResources().getString(R.string.enqueue_on_background),
|
StreamDialogEntry.setEnabledEntries(
|
||||||
context.getResources().getString(R.string.enqueue_on_popup),
|
StreamDialogEntry.enqueue_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_main),
|
StreamDialogEntry.start_here_on_background,
|
||||||
context.getResources().getString(R.string.start_here_on_background),
|
StreamDialogEntry.set_as_playlist_thumbnail,
|
||||||
context.getResources().getString(R.string.start_here_on_popup),
|
StreamDialogEntry.delete,
|
||||||
context.getResources().getString(R.string.set_as_playlist_thumbnail),
|
StreamDialogEntry.append_playlist,
|
||||||
context.getResources().getString(R.string.delete),
|
StreamDialogEntry.share);
|
||||||
context.getResources().getString(R.string.share)
|
} else {
|
||||||
};
|
StreamDialogEntry.setEnabledEntries(
|
||||||
|
StreamDialogEntry.enqueue_on_background,
|
||||||
|
StreamDialogEntry.enqueue_on_popup,
|
||||||
|
StreamDialogEntry.start_here_on_background,
|
||||||
|
StreamDialogEntry.start_here_on_popup,
|
||||||
|
StreamDialogEntry.set_as_playlist_thumbnail,
|
||||||
|
StreamDialogEntry.delete,
|
||||||
|
StreamDialogEntry.append_playlist,
|
||||||
|
StreamDialogEntry.share);
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
StreamDialogEntry.start_here_on_popup.setCustomAction(
|
||||||
final int index = Math.max(itemListAdapter.getItemsList().indexOf(item), 0);
|
(fragment, infoItemDuplicate) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(context,
|
|
||||||
new SinglePlayQueue(infoItem));
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, new
|
|
||||||
SinglePlayQueue(infoItem));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
changeThumbnailUrl(item.thumbnailUrl);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
deleteItem(item);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
new InfoItemDialog(getActivity(), infoItem, commands, actions).show();
|
StreamDialogEntry.start_here_on_background.setCustomAction(
|
||||||
|
(fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
|
||||||
|
StreamDialogEntry.set_as_playlist_thumbnail.setCustomAction(
|
||||||
|
(fragment, infoItemDuplicate) -> changeThumbnailUrl(item.thumbnailUrl));
|
||||||
|
StreamDialogEntry.delete.setCustomAction(
|
||||||
|
(fragment, infoItemDuplicate) -> deleteItem(item));
|
||||||
|
|
||||||
|
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||||
|
StreamDialogEntry.clickOn(which, this, infoItem)).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInitialData(long playlistId, String name) {
|
private void setInitialData(long playlistId, String name) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.schabi.newpipe.local.playlist;
|
package org.schabi.newpipe.local.playlist;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.AppDatabase;
|
import org.schabi.newpipe.database.AppDatabase;
|
||||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||||
|
|
|
@ -4,10 +4,10 @@ import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.schabi.newpipe.local.subscription;
|
package org.schabi.newpipe.local.subscription;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.grack.nanojson.JsonAppendableWriter;
|
import com.grack.nanojson.JsonAppendableWriter;
|
||||||
import com.grack.nanojson.JsonArray;
|
import com.grack.nanojson.JsonArray;
|
||||||
|
|
|
@ -17,16 +17,15 @@ 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.preference.PreferenceManager;
|
||||||
import android.support.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -48,15 +47,14 @@ 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.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.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;
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
|
import org.schabi.newpipe.util.ShareUtils;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
import org.schabi.newpipe.views.CollapsibleView;
|
import org.schabi.newpipe.views.CollapsibleView;
|
||||||
|
|
||||||
|
@ -130,6 +128,11 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||||
subscriptionService = SubscriptionService.getInstance(activity);
|
subscriptionService = SubscriptionService.getInstance(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -376,7 +379,6 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
whatsNewItemListHeader.setOnClickListener(v -> {
|
whatsNewItemListHeader.setOnClickListener(v -> {
|
||||||
FragmentManager fragmentManager = getFM();
|
FragmentManager fragmentManager = getFM();
|
||||||
NavigationHelper.openWhatsNewFragment(fragmentManager);
|
NavigationHelper.openWhatsNewFragment(fragmentManager);
|
||||||
|
@ -390,17 +392,17 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||||
if (context == null || context.getResources() == null || getActivity() == null) return;
|
if (context == null || context.getResources() == null || getActivity() == null) return;
|
||||||
|
|
||||||
final String[] commands = new String[]{
|
final String[] commands = new String[]{
|
||||||
context.getResources().getString(R.string.share),
|
context.getResources().getString(R.string.unsubscribe),
|
||||||
context.getResources().getString(R.string.unsubscribe)
|
context.getResources().getString(R.string.share)
|
||||||
};
|
};
|
||||||
|
|
||||||
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
shareChannel(selectedItem);
|
deleteChannel(selectedItem);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
deleteChannel(selectedItem);
|
shareChannel(selectedItem);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -425,7 +427,7 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shareChannel(ChannelInfoItem selectedItem) {
|
private void shareChannel(ChannelInfoItem selectedItem) {
|
||||||
shareUrl(selectedItem.getName(), selectedItem.getUrl());
|
ShareUtils.shareUrl(getContext(), selectedItem.getName(), selectedItem.getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.schabi.newpipe.local.subscription;
|
package org.schabi.newpipe.local.subscription;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
@ -93,7 +93,7 @@ public class SubscriptionService {
|
||||||
* in the cooldown interval, only the latest changes are emitted to the subscribers.
|
* in the cooldown interval, only the latest changes are emitted to the subscribers.
|
||||||
* This reduces the amount of observations caused by frequent updates to the database.
|
* This reduces the amount of observations caused by frequent updates to the database.
|
||||||
*/
|
*/
|
||||||
@android.support.annotation.NonNull
|
@androidx.annotation.NonNull
|
||||||
public Flowable<List<SubscriptionEntity>> getSubscription() {
|
public Flowable<List<SubscriptionEntity>> getSubscription() {
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ package org.schabi.newpipe.local.subscription;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import android.support.v4.text.util.LinkifyCompat;
|
import androidx.core.text.util.LinkifyCompat;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
|
@ -23,11 +23,11 @@ import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import android.support.v4.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
package org.schabi.newpipe.local.subscription.services;
|
package org.schabi.newpipe.local.subscription.services;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue