mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-11 23:07:37 +01:00
Compare commits
166 Commits
v0.19.4
...
v0.19.6-fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ade5e38fa5 | ||
|
|
c900ef036c | ||
|
|
d088d432c5 | ||
|
|
c9fafbe198 | ||
|
|
2d909b0514 | ||
|
|
c2a012553d | ||
|
|
085f0266ac | ||
|
|
7ede2daa3c | ||
|
|
713c53d170 | ||
|
|
110b3a6a8f | ||
|
|
e12e6dd7a7 | ||
|
|
e183fc6118 | ||
|
|
dd57e246b8 | ||
|
|
f4a4172369 | ||
|
|
b96d1714b5 | ||
|
|
dbd809b040 | ||
|
|
ff4e6b139d | ||
|
|
af098aaac8 | ||
|
|
5d7e62c736 | ||
|
|
e2ead011f5 | ||
|
|
a067c950e1 | ||
|
|
3369618cfa | ||
|
|
4e9b6520e5 | ||
|
|
a074203fae | ||
|
|
42092e3f28 | ||
|
|
c07b34e298 | ||
|
|
ef5f181328 | ||
|
|
a7ea2fcf92 | ||
|
|
e81730715c | ||
|
|
c09f2ad482 | ||
|
|
84aef8b5b5 | ||
|
|
8fac3e8221 | ||
|
|
5874ed781d | ||
|
|
d8f29bd7a7 | ||
|
|
8120b6aaaa | ||
|
|
13a0d1de70 | ||
|
|
20e828be51 | ||
|
|
ccd82fb8b8 | ||
|
|
0711650ff8 | ||
|
|
4194ac2226 | ||
|
|
248e2d7ee0 | ||
|
|
1daa654051 | ||
|
|
d751434979 | ||
|
|
8cc21920b7 | ||
|
|
248212588d | ||
|
|
13c0fdef08 | ||
|
|
dfc27b2480 | ||
|
|
b2d78d380b | ||
|
|
faa6cb5c7d | ||
|
|
452977abdf | ||
|
|
93570b2f59 | ||
|
|
073f5c2c8c | ||
|
|
1b4313f847 | ||
|
|
07cead7e99 | ||
|
|
f128751aba | ||
|
|
9516d9da17 | ||
|
|
e9aafc2a56 | ||
|
|
a91a6575e0 | ||
|
|
5bd4093dfb | ||
|
|
f9890e2016 | ||
|
|
00529fe134 | ||
|
|
5e9dce7d39 | ||
|
|
734680b9f0 | ||
|
|
d0b5345252 | ||
|
|
c2b4a44a59 | ||
|
|
38c79bbc11 | ||
|
|
0d7028a36c | ||
|
|
b63f687491 | ||
|
|
952b636569 | ||
|
|
e14ba48244 | ||
|
|
720c8c31ec | ||
|
|
4e8407ed8f | ||
|
|
26c5f69161 | ||
|
|
078ae15794 | ||
|
|
16ae90dc9f | ||
|
|
3de5afc68e | ||
|
|
a7e8f5087e | ||
|
|
2e6e75cd4e | ||
|
|
9f3b35634a | ||
|
|
c24dfc63dc | ||
|
|
c029929850 | ||
|
|
d9100913d5 | ||
|
|
e03d970bc2 | ||
|
|
fe4516ea23 | ||
|
|
039b47b872 | ||
|
|
0a57a8a7f3 | ||
|
|
8c823f3a2d | ||
|
|
33f3a4f455 | ||
|
|
2ecf8044d7 | ||
|
|
c9a1eb55b5 | ||
|
|
76bb1fd61e | ||
|
|
1275b26ba0 | ||
|
|
a470a4af9b | ||
|
|
487c9ebbd4 | ||
|
|
746cab92f0 | ||
|
|
33266a96ff | ||
|
|
2816889d8d | ||
|
|
58e177b3e4 | ||
|
|
5cfd8bbb56 | ||
|
|
e6fe6fd645 | ||
|
|
63e167b38e | ||
|
|
abe77c4783 | ||
|
|
72af51fe9d | ||
|
|
36b4134838 | ||
|
|
cf6ee26fdb | ||
|
|
858111e623 | ||
|
|
367e625804 | ||
|
|
ae4d9c7f80 | ||
|
|
6c6ee41346 | ||
|
|
9ef7688f9e | ||
|
|
7d6e226c2b | ||
|
|
17d1346a8a | ||
|
|
59e0c10c42 | ||
|
|
0d29e66092 | ||
|
|
caa000f447 | ||
|
|
267e114354 | ||
|
|
b5375396d2 | ||
|
|
e34f666b70 | ||
|
|
19334b4f96 | ||
|
|
1fa609e539 | ||
|
|
d9ce25a721 | ||
|
|
5b8fc25da6 | ||
|
|
c70968dcf1 | ||
|
|
77147510fb | ||
|
|
7092577482 | ||
|
|
3e70050056 | ||
|
|
1f23c814e5 | ||
|
|
145e0a0b7b | ||
|
|
e68e787e7a | ||
|
|
903308d285 | ||
|
|
a4d8388b2e | ||
|
|
3e83f9f956 | ||
|
|
4063221313 | ||
|
|
c3c8d80919 | ||
|
|
3ae71c73c4 | ||
|
|
b3db8c9549 | ||
|
|
596eb4a0f9 | ||
|
|
6b0381b903 | ||
|
|
049c8f70cd | ||
|
|
353bf69550 | ||
|
|
cdb989ede3 | ||
|
|
74079e4238 | ||
|
|
c89746214c | ||
|
|
1d97db3ef9 | ||
|
|
e43fdf5ef9 | ||
|
|
3984fc075f | ||
|
|
ee2a159374 | ||
|
|
57c9b29ba3 | ||
|
|
927ea72337 | ||
|
|
123bdbd13b | ||
|
|
0baa5a2f04 | ||
|
|
7d98a70028 | ||
|
|
73b72ab01c | ||
|
|
e8cf71f41c | ||
|
|
5f2d2a64d2 | ||
|
|
27bf3901de | ||
|
|
16d4fa03a5 | ||
|
|
bef579ec26 | ||
|
|
0cff10e02e | ||
|
|
4c6f7238dd | ||
|
|
55027a9b2b | ||
|
|
0a984ca8c8 | ||
|
|
929d13bfea | ||
|
|
1975973ff2 | ||
|
|
63afacc067 | ||
|
|
3175199787 |
@@ -13,8 +13,8 @@ android {
|
|||||||
resValue "string", "app_name", "NewPipe"
|
resValue "string", "app_name", "NewPipe"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 940
|
versionCode 951
|
||||||
versionName "0.19.4"
|
versionName "0.19.6"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -50,7 +50,7 @@ android {
|
|||||||
// TODO: update Gradle version
|
// TODO: update Gradle version
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources false // disabled to fix F-Droid's reproducible build
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
archivesBaseName = 'app'
|
archivesBaseName = 'app'
|
||||||
}
|
}
|
||||||
@@ -84,13 +84,18 @@ ext {
|
|||||||
checkstyleVersion = '8.32'
|
checkstyleVersion = '8.32'
|
||||||
stethoVersion = '1.5.1'
|
stethoVersion = '1.5.1'
|
||||||
leakCanaryVersion = '2.2'
|
leakCanaryVersion = '2.2'
|
||||||
exoPlayerVersion = '2.11.4'
|
exoPlayerVersion = '2.11.6'
|
||||||
androidxLifecycleVersion = '2.2.0'
|
androidxLifecycleVersion = '2.2.0'
|
||||||
androidxRoomVersion = '2.2.5'
|
androidxRoomVersion = '2.2.5'
|
||||||
groupieVersion = '2.8.0'
|
groupieVersion = '2.8.0'
|
||||||
markwonVersion = '4.3.1'
|
markwonVersion = '4.3.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
checkstyle
|
||||||
|
ktlint
|
||||||
|
}
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
configFile rootProject.file('checkstyle.xml')
|
configFile rootProject.file('checkstyle.xml')
|
||||||
ignoreFailures false
|
ignoreFailures false
|
||||||
@@ -106,8 +111,7 @@ task runCheckstyle(type: Checkstyle) {
|
|||||||
exclude '**/BuildConfig.java'
|
exclude '**/BuildConfig.java'
|
||||||
exclude 'main/java/us/shandian/giga/**'
|
exclude 'main/java/us/shandian/giga/**'
|
||||||
|
|
||||||
// empty classpath
|
classpath = configurations.checkstyle
|
||||||
classpath = files()
|
|
||||||
|
|
||||||
showViolations true
|
showViolations true
|
||||||
|
|
||||||
@@ -117,10 +121,6 @@ task runCheckstyle(type: Checkstyle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
|
||||||
ktlint
|
|
||||||
}
|
|
||||||
|
|
||||||
task runKtlint(type: JavaExec) {
|
task runKtlint(type: JavaExec) {
|
||||||
main = "com.pinterest.ktlint.Main"
|
main = "com.pinterest.ktlint.Main"
|
||||||
classpath = configurations.ktlint
|
classpath = configurations.ktlint
|
||||||
@@ -143,7 +143,7 @@ dependencies {
|
|||||||
implementation "frankiesardo:icepick:${icepickVersion}"
|
implementation "frankiesardo:icepick:${icepickVersion}"
|
||||||
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
||||||
|
|
||||||
debugImplementation "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
ktlint "com.pinterest:ktlint:0.35.0"
|
ktlint "com.pinterest:ktlint:0.35.0"
|
||||||
|
|
||||||
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
||||||
@@ -163,7 +163,7 @@ dependencies {
|
|||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:98055a3c3c17f2543a63d375a44c1d1f557fa76e'
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:a70cb0283ffc3bba2709815673a5a7940aab0a3a'
|
||||||
|
|
||||||
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
|
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
|
||||||
implementation "org.jsoup:jsoup:1.13.1"
|
implementation "org.jsoup:jsoup:1.13.1"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,8 +34,6 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -127,12 +125,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
Window w = getWindow();
|
|
||||||
w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
|
|
||||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getSupportFragmentManager() != null
|
if (getSupportFragmentManager() != null
|
||||||
&& getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
&& getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
||||||
initFragments();
|
initFragments();
|
||||||
@@ -166,6 +158,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
.add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator
|
.add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator
|
||||||
.getTranslatedKioskName(ks, this))
|
.getTranslatedKioskName(ks, this))
|
||||||
.setIcon(KioskTranslator.getKioskIcon(ks, this));
|
.setIcon(KioskTranslator.getKioskIcon(ks, this));
|
||||||
|
kioskId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawerItems.getMenu()
|
drawerItems.getMenu()
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package org.schabi.newpipe.about;
|
package org.schabi.newpipe.about;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -26,6 +24,7 @@ import org.schabi.newpipe.R;
|
|||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
import static org.schabi.newpipe.util.ShareUtils.openUrlInBrowser;
|
||||||
|
|
||||||
public class AboutActivity extends AppCompatActivity {
|
public class AboutActivity extends AppCompatActivity {
|
||||||
/**
|
/**
|
||||||
@@ -143,28 +142,23 @@ public class AboutActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
View githubLink = rootView.findViewById(R.id.github_link);
|
View githubLink = rootView.findViewById(R.id.github_link);
|
||||||
githubLink.setOnClickListener(nv ->
|
githubLink.setOnClickListener(nv ->
|
||||||
openWebsite(context.getString(R.string.github_url), context));
|
openUrlInBrowser(context, context.getString(R.string.github_url)));
|
||||||
|
|
||||||
View donationLink = rootView.findViewById(R.id.donation_link);
|
View donationLink = rootView.findViewById(R.id.donation_link);
|
||||||
donationLink.setOnClickListener(v ->
|
donationLink.setOnClickListener(v ->
|
||||||
openWebsite(context.getString(R.string.donation_url), context));
|
openUrlInBrowser(context, context.getString(R.string.donation_url)));
|
||||||
|
|
||||||
View websiteLink = rootView.findViewById(R.id.website_link);
|
View websiteLink = rootView.findViewById(R.id.website_link);
|
||||||
websiteLink.setOnClickListener(nv ->
|
websiteLink.setOnClickListener(nv ->
|
||||||
openWebsite(context.getString(R.string.website_url), context));
|
openUrlInBrowser(context, context.getString(R.string.website_url)));
|
||||||
|
|
||||||
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
|
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
|
||||||
privacyPolicyLink.setOnClickListener(v ->
|
privacyPolicyLink.setOnClickListener(v ->
|
||||||
openWebsite(context.getString(R.string.privacy_policy_url), context));
|
openUrlInBrowser(context, context.getString(R.string.privacy_policy_url)));
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openWebsite(final String url, final Context context) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,33 @@
|
|||||||
package org.schabi.newpipe.database.playlist;
|
package org.schabi.newpipe.database.playlist;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
|
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface PlaylistLocalItem extends LocalItem {
|
public interface PlaylistLocalItem extends LocalItem {
|
||||||
String getOrderingName();
|
String getOrderingName();
|
||||||
|
|
||||||
|
static List<PlaylistLocalItem> merge(
|
||||||
|
final List<PlaylistMetadataEntry> localPlaylists,
|
||||||
|
final List<PlaylistRemoteEntity> remotePlaylists) {
|
||||||
|
final List<PlaylistLocalItem> items = new ArrayList<>(
|
||||||
|
localPlaylists.size() + remotePlaylists.size());
|
||||||
|
items.addAll(localPlaylists);
|
||||||
|
items.addAll(remotePlaylists);
|
||||||
|
|
||||||
|
Collections.sort(items, (left, right) -> {
|
||||||
|
final String on1 = left.getOrderingName();
|
||||||
|
final String on2 = right.getOrderingName();
|
||||||
|
if (on1 == null) {
|
||||||
|
return on2 == null ? 0 : 1;
|
||||||
|
} else {
|
||||||
|
return on2 == null ? -1 : on1.compareToIgnoreCase(on2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,45 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
|||||||
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
|
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
|
||||||
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
|
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT * FROM subscriptions
|
||||||
|
|
||||||
|
WHERE name LIKE '%' || :filter || '%'
|
||||||
|
|
||||||
|
ORDER BY name COLLATE NOCASE ASC
|
||||||
|
""")
|
||||||
|
abstract fun getSubscriptionsFiltered(filter: String): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT * FROM subscriptions s
|
||||||
|
|
||||||
|
LEFT JOIN feed_group_subscription_join fgs
|
||||||
|
ON s.uid = fgs.subscription_id
|
||||||
|
|
||||||
|
WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
|
||||||
|
|
||||||
|
ORDER BY name COLLATE NOCASE ASC
|
||||||
|
""")
|
||||||
|
abstract fun getSubscriptionsOnlyUngrouped(
|
||||||
|
currentGroupId: Long
|
||||||
|
): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT * FROM subscriptions s
|
||||||
|
|
||||||
|
LEFT JOIN feed_group_subscription_join fgs
|
||||||
|
ON s.uid = fgs.subscription_id
|
||||||
|
|
||||||
|
WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
|
||||||
|
AND s.name LIKE '%' || :filter || '%'
|
||||||
|
|
||||||
|
ORDER BY name COLLATE NOCASE ASC
|
||||||
|
""")
|
||||||
|
abstract fun getSubscriptionsOnlyUngroupedFiltered(
|
||||||
|
currentGroupId: Long,
|
||||||
|
filter: String
|
||||||
|
): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
|
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
|
||||||
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
|
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
@@ -52,7 +91,7 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
|||||||
entity.uid = uidFromInsert
|
entity.uid = uidFromInsert
|
||||||
} else {
|
} else {
|
||||||
val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url)
|
val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url)
|
||||||
?: throw IllegalStateException("Subscription cannot be null just after insertion.")
|
?: throw IllegalStateException("Subscription cannot be null just after insertion.")
|
||||||
entity.uid = subscriptionIdFromDb
|
entity.uid = subscriptionIdFromDb
|
||||||
|
|
||||||
update(entity)
|
update(entity)
|
||||||
|
|||||||
@@ -130,4 +130,55 @@ public class SubscriptionEntity {
|
|||||||
item.setDescription(getDescription());
|
item.setDescription(getDescription());
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Remove these generated methods by migrating this class to a data class from Kotlin.
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("EqualsReplaceableByObjectsCall")
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SubscriptionEntity that = (SubscriptionEntity) o;
|
||||||
|
|
||||||
|
if (uid != that.uid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serviceId != that.serviceId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!url.equals(that.url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name != null ? !name.equals(that.name) : that.name != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (subscriberCount != null
|
||||||
|
? !subscriberCount.equals(that.subscriberCount)
|
||||||
|
: that.subscriberCount != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return description != null
|
||||||
|
? description.equals(that.description)
|
||||||
|
: that.description == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = (int) (uid ^ (uid >>> 32));
|
||||||
|
result = 31 * result + serviceId;
|
||||||
|
result = 31 * result + url.hashCode();
|
||||||
|
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||||
|
result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0);
|
||||||
|
result = 31 * result + (subscriberCount != null ? subscriberCount.hashCode() : 0);
|
||||||
|
result = 31 * result + (description != null ? description.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -479,7 +479,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
|
|||||||
case R.id.detail_controls_download:
|
case R.id.detail_controls_download:
|
||||||
NavigationHelper.openDownloads(getActivity());
|
NavigationHelper.openDownloads(getActivity());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R.id.detail_uploader_root_layout:
|
case R.id.detail_uploader_root_layout:
|
||||||
if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) {
|
if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) {
|
||||||
Log.w(TAG,
|
Log.w(TAG,
|
||||||
@@ -488,6 +487,9 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
|
|||||||
openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName());
|
openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case R.id.detail_title_root_layout:
|
||||||
|
ShareUtils.copyToClipboard(getContext(), videoTitleTextView.getText().toString());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -583,6 +585,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
|
|||||||
protected void initListeners() {
|
protected void initListeners() {
|
||||||
super.initListeners();
|
super.initListeners();
|
||||||
|
|
||||||
|
videoTitleRoot.setOnLongClickListener(this);
|
||||||
uploaderRootLayout.setOnClickListener(this);
|
uploaderRootLayout.setOnClickListener(this);
|
||||||
uploaderRootLayout.setOnLongClickListener(this);
|
uploaderRootLayout.setOnLongClickListener(this);
|
||||||
videoTitleRoot.setOnClickListener(this);
|
videoTitleRoot.setOnClickListener(this);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ 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.extractor.Page;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.views.NewPipeRecyclerView;
|
import org.schabi.newpipe.views.NewPipeRecyclerView;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
protected String url;
|
protected String url;
|
||||||
|
|
||||||
protected I currentInfo;
|
protected I currentInfo;
|
||||||
protected String currentNextPageUrl;
|
protected Page currentNextPage;
|
||||||
protected Disposable currentWorker;
|
protected Disposable currentWorker;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,7 +79,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
public void writeTo(final Queue<Object> objectsToSave) {
|
public void writeTo(final Queue<Object> objectsToSave) {
|
||||||
super.writeTo(objectsToSave);
|
super.writeTo(objectsToSave);
|
||||||
objectsToSave.add(currentInfo);
|
objectsToSave.add(currentInfo);
|
||||||
objectsToSave.add(currentNextPageUrl);
|
objectsToSave.add(currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,7 +87,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
||||||
super.readFrom(savedObjects);
|
super.readFrom(savedObjects);
|
||||||
currentInfo = (I) savedObjects.poll();
|
currentInfo = (I) savedObjects.poll();
|
||||||
currentNextPageUrl = (String) savedObjects.poll();
|
currentNextPage = (Page) savedObjects.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -130,7 +131,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
.subscribe((@NonNull I result) -> {
|
.subscribe((@NonNull I result) -> {
|
||||||
isLoading.set(false);
|
isLoading.set(false);
|
||||||
currentInfo = result;
|
currentInfo = result;
|
||||||
currentNextPageUrl = result.getNextPageUrl();
|
currentNextPage = result.getNextPage();
|
||||||
handleResult(result);
|
handleResult(result);
|
||||||
}, (@NonNull Throwable throwable) -> onError(throwable));
|
}, (@NonNull Throwable throwable) -> onError(throwable));
|
||||||
}
|
}
|
||||||
@@ -182,7 +183,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
@Override
|
@Override
|
||||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||||
super.handleNextItems(result);
|
super.handleNextItems(result);
|
||||||
currentNextPageUrl = result.getNextPageUrl();
|
currentNextPage = result.getNextPage();
|
||||||
infoListAdapter.addInfoItemList(result.getItems());
|
infoListAdapter.addInfoItemList(result.getItems());
|
||||||
|
|
||||||
showListFooter(hasMoreItems());
|
showListFooter(hasMoreItems());
|
||||||
@@ -190,7 +191,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasMoreItems() {
|
protected boolean hasMoreItems() {
|
||||||
return !TextUtils.isEmpty(currentNextPageUrl);
|
return Page.isValid(currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
||||||
return ExtractorHelper.getMoreChannelItems(serviceId, url, currentNextPageUrl);
|
return ExtractorHelper.getMoreChannelItems(serviceId, url, currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -555,7 +555,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
|
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
|
||||||
currentInfo.getNextPageUrl(), streamItems, index);
|
currentInfo.getNextPage(), streamItems, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
||||||
return ExtractorHelper.getMoreCommentItems(serviceId, currentInfo, currentNextPageUrl);
|
return ExtractorHelper.getMoreCommentItems(serviceId, currentInfo, currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class DefaultKioskFragment extends KioskFragment {
|
|||||||
name = kioskTranslatedName;
|
name = kioskTranslatedName;
|
||||||
|
|
||||||
currentInfo = null;
|
currentInfo = null;
|
||||||
currentNextPageUrl = null;
|
currentNextPage = null;
|
||||||
} catch (ExtractionException e) {
|
} catch (ExtractionException e) {
|
||||||
onUnrecoverableError(e, UserAction.REQUESTED_KIOSK, "none",
|
onUnrecoverableError(e, UserAction.REQUESTED_KIOSK, "none",
|
||||||
"Loading default kiosk from selected service", 0);
|
"Loading default kiosk from selected service", 0);
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
||||||
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl);
|
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
||||||
return ExtractorHelper.getMorePlaylistItems(serviceId, url, currentNextPageUrl);
|
return ExtractorHelper.getMorePlaylistItems(serviceId, url, currentNextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -349,7 +349,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
return new PlaylistPlayQueue(
|
return new PlaylistPlayQueue(
|
||||||
currentInfo.getServiceId(),
|
currentInfo.getServiceId(),
|
||||||
currentInfo.getUrl(),
|
currentInfo.getUrl(),
|
||||||
currentInfo.getNextPageUrl(),
|
currentInfo.getNextPage(),
|
||||||
infoItems,
|
infoItems,
|
||||||
index
|
index
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -37,6 +38,7 @@ import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
|||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.Page;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||||
@@ -71,6 +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.text.Html.escapeHtml;
|
||||||
import static androidx.recyclerview.widget.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;
|
||||||
@@ -118,13 +121,18 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
@State
|
@State
|
||||||
String lastSearchedString;
|
String lastSearchedString;
|
||||||
|
|
||||||
|
@State
|
||||||
|
String searchSuggestion;
|
||||||
|
|
||||||
|
@State
|
||||||
|
boolean isCorrectedSearch;
|
||||||
|
|
||||||
@State
|
@State
|
||||||
boolean wasSearchFocused = false;
|
boolean wasSearchFocused = false;
|
||||||
|
|
||||||
private Map<Integer, String> menuItemToFilterName;
|
private Map<Integer, String> menuItemToFilterName;
|
||||||
private StreamingService service;
|
private StreamingService service;
|
||||||
private String currentPageUrl;
|
private Page nextPage;
|
||||||
private String nextPageUrl;
|
|
||||||
private String contentCountry;
|
private String contentCountry;
|
||||||
private boolean isSuggestionsEnabled = true;
|
private boolean isSuggestionsEnabled = true;
|
||||||
|
|
||||||
@@ -143,6 +151,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
private EditText searchEditText;
|
private EditText searchEditText;
|
||||||
private View searchClear;
|
private View searchClear;
|
||||||
|
|
||||||
|
private TextView correctSuggestion;
|
||||||
|
|
||||||
private View suggestionsPanel;
|
private View suggestionsPanel;
|
||||||
private RecyclerView suggestionsRecyclerView;
|
private RecyclerView suggestionsRecyclerView;
|
||||||
|
|
||||||
@@ -257,6 +267,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSearchSuggestion();
|
||||||
|
|
||||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
||||||
initSuggestionObserver();
|
initSuggestionObserver();
|
||||||
}
|
}
|
||||||
@@ -345,6 +357,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
|
searchToolbarContainer = activity.findViewById(R.id.toolbar_search_container);
|
||||||
searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
|
searchEditText = searchToolbarContainer.findViewById(R.id.toolbar_search_edit_text);
|
||||||
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
|
searchClear = searchToolbarContainer.findViewById(R.id.toolbar_search_clear);
|
||||||
|
|
||||||
|
correctSuggestion = rootView.findViewById(R.id.correct_suggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -354,15 +368,13 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
@Override
|
@Override
|
||||||
public void writeTo(final Queue<Object> objectsToSave) {
|
public void writeTo(final Queue<Object> objectsToSave) {
|
||||||
super.writeTo(objectsToSave);
|
super.writeTo(objectsToSave);
|
||||||
objectsToSave.add(currentPageUrl);
|
objectsToSave.add(nextPage);
|
||||||
objectsToSave.add(nextPageUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
||||||
super.readFrom(savedObjects);
|
super.readFrom(savedObjects);
|
||||||
currentPageUrl = (String) savedObjects.poll();
|
nextPage = (Page) savedObjects.poll();
|
||||||
nextPageUrl = (String) savedObjects.poll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -497,6 +509,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
correctSuggestion.setVisibility(View.GONE);
|
||||||
|
|
||||||
searchEditText.setText("");
|
searchEditText.setText("");
|
||||||
suggestionListAdapter.setItems(new ArrayList<>());
|
suggestionListAdapter.setItems(new ArrayList<>());
|
||||||
showKeyboardSearch();
|
showKeyboardSearch();
|
||||||
@@ -554,11 +568,13 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
textWatcher = new TextWatcher() {
|
textWatcher = new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(final CharSequence s, final int start,
|
public void beforeTextChanged(final CharSequence s, final int start,
|
||||||
final int count, final int after) { }
|
final int count, final int after) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(final CharSequence s, final int start,
|
public void onTextChanged(final CharSequence s, final int start,
|
||||||
final int before, final int count) { }
|
final int before, final int count) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(final Editable s) {
|
public void afterTextChanged(final Editable s) {
|
||||||
@@ -688,10 +704,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void giveSearchEditTextFocus() {
|
|
||||||
showKeyboardSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initSuggestionObserver() {
|
private void initSuggestionObserver() {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "initSuggestionObserver() called");
|
Log.d(TAG, "initSuggestionObserver() called");
|
||||||
@@ -845,7 +857,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadMoreItems() {
|
protected void loadMoreItems() {
|
||||||
if (nextPageUrl == null || nextPageUrl.isEmpty()) {
|
if (!Page.isValid(nextPage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isLoading.set(true);
|
isLoading.set(true);
|
||||||
@@ -858,7 +870,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
searchString,
|
searchString,
|
||||||
asList(contentFilter),
|
asList(contentFilter),
|
||||||
sortFilter,
|
sortFilter,
|
||||||
nextPageUrl)
|
nextPage)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
|
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
|
||||||
@@ -961,9 +973,13 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
NewPipe.getNameOfService(serviceId), searchString, 0);
|
NewPipe.getNameOfService(serviceId), searchString, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchSuggestion = result.getSearchSuggestion();
|
||||||
|
isCorrectedSearch = result.isCorrectedSearch();
|
||||||
|
|
||||||
|
handleSearchSuggestion();
|
||||||
|
|
||||||
lastSearchedString = searchString;
|
lastSearchedString = searchString;
|
||||||
nextPageUrl = result.getNextPageUrl();
|
nextPage = result.getNextPage();
|
||||||
currentPageUrl = result.getUrl();
|
|
||||||
|
|
||||||
if (infoListAdapter.getItemsList().size() == 0) {
|
if (infoListAdapter.getItemsList().size() == 0) {
|
||||||
if (!result.getRelatedItems().isEmpty()) {
|
if (!result.getRelatedItems().isEmpty()) {
|
||||||
@@ -978,17 +994,49 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
super.handleResult(result);
|
super.handleResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSearchSuggestion() {
|
||||||
|
if (TextUtils.isEmpty(searchSuggestion)) {
|
||||||
|
correctSuggestion.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
final String helperText = getString(isCorrectedSearch
|
||||||
|
? R.string.search_showing_result_for
|
||||||
|
: R.string.did_you_mean);
|
||||||
|
|
||||||
|
final String highlightedSearchSuggestion =
|
||||||
|
"<b><i>" + escapeHtml(searchSuggestion) + "</i></b>";
|
||||||
|
correctSuggestion.setText(
|
||||||
|
Html.fromHtml(String.format(helperText, highlightedSearchSuggestion)));
|
||||||
|
|
||||||
|
|
||||||
|
correctSuggestion.setOnClickListener(v -> {
|
||||||
|
correctSuggestion.setVisibility(View.GONE);
|
||||||
|
search(searchSuggestion, contentFilter, sortFilter);
|
||||||
|
searchEditText.setText(searchSuggestion);
|
||||||
|
});
|
||||||
|
|
||||||
|
correctSuggestion.setOnLongClickListener(v -> {
|
||||||
|
searchEditText.setText(searchSuggestion);
|
||||||
|
searchEditText.setSelection(searchSuggestion.length());
|
||||||
|
showKeyboardSearch();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
correctSuggestion.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||||
showListFooter(false);
|
showListFooter(false);
|
||||||
currentPageUrl = result.getNextPageUrl();
|
|
||||||
infoListAdapter.addInfoItemList(result.getItems());
|
infoListAdapter.addInfoItemList(result.getItems());
|
||||||
nextPageUrl = result.getNextPageUrl();
|
nextPage = result.getNextPage();
|
||||||
|
|
||||||
if (!result.getErrors().isEmpty()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
||||||
NewPipe.getNameOfService(serviceId),
|
NewPipe.getNameOfService(serviceId),
|
||||||
"\"" + searchString + "\" → page: " + nextPageUrl, 0);
|
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
|
||||||
|
+ "pageIds: " + nextPage.getIds() + ", "
|
||||||
|
+ "pageCookies: " + nextPage.getCookies(), 0);
|
||||||
}
|
}
|
||||||
super.handleNextItems(result);
|
super.handleNextItems(result);
|
||||||
}
|
}
|
||||||
@@ -1020,6 +1068,10 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
|
public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
|
||||||
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getAdapterPosition();
|
||||||
|
if (position == RecyclerView.NO_POSITION) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
final SuggestionItem item = suggestionListAdapter.getItem(position);
|
final SuggestionItem item = suggestionListAdapter.getItem(position);
|
||||||
return item.fromHistory ? makeMovementFlags(0,
|
return item.fromHistory ? makeMovementFlags(0,
|
||||||
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package org.schabi.newpipe.info_list.holder;
|
package org.schabi.newpipe.info_list.holder;
|
||||||
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.text.style.URLSpan;
|
import android.text.style.URLSpan;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
@@ -24,6 +20,7 @@ import org.schabi.newpipe.util.CommentTextOnTouchListener;
|
|||||||
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.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -129,14 +126,10 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
|||||||
|
|
||||||
|
|
||||||
itemView.setOnLongClickListener(view -> {
|
itemView.setOnLongClickListener(view -> {
|
||||||
if (!AndroidTvUtils.isTv(itemBuilder.getContext())) {
|
if (AndroidTvUtils.isTv(itemBuilder.getContext())) {
|
||||||
ClipboardManager clipboardManager = (ClipboardManager) itemBuilder.getContext()
|
|
||||||
.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, commentText));
|
|
||||||
Toast.makeText(itemBuilder.getContext(), R.string.msg_copied, Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
openCommentAuthor(item);
|
openCommentAuthor(item);
|
||||||
|
} else {
|
||||||
|
ShareUtils.copyToClipboard(itemBuilder.getContext(), commentText);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ import org.schabi.newpipe.report.UserAction;
|
|||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
@@ -54,31 +52,6 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
|
|||||||
// Fragment LifeCycle - Creation
|
// Fragment LifeCycle - Creation
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private static List<PlaylistLocalItem> merge(
|
|
||||||
final List<PlaylistMetadataEntry> localPlaylists,
|
|
||||||
final List<PlaylistRemoteEntity> remotePlaylists) {
|
|
||||||
List<PlaylistLocalItem> items = new ArrayList<>(
|
|
||||||
localPlaylists.size() + remotePlaylists.size());
|
|
||||||
items.addAll(localPlaylists);
|
|
||||||
items.addAll(remotePlaylists);
|
|
||||||
|
|
||||||
Collections.sort(items, (left, right) -> {
|
|
||||||
String on1 = left.getOrderingName();
|
|
||||||
String on2 = right.getOrderingName();
|
|
||||||
if (on1 == null && on2 == null) {
|
|
||||||
return 0;
|
|
||||||
} else if (on1 != null && on2 == null) {
|
|
||||||
return -1;
|
|
||||||
} else if (on1 == null && on2 != null) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return on1.compareToIgnoreCase(on2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(final Bundle savedInstanceState) {
|
public void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -164,7 +137,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
|
|||||||
super.startLoading(forceLoad);
|
super.startLoading(forceLoad);
|
||||||
|
|
||||||
Flowable.combineLatest(localPlaylistManager.getPlaylists(),
|
Flowable.combineLatest(localPlaylistManager.getPlaylists(),
|
||||||
remotePlaylistManager.getPlaylists(), BookmarkFragment::merge)
|
remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge)
|
||||||
.onBackpressureLatest()
|
.onBackpressureLatest()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(getPlaylistsSubscriber());
|
.subscribe(getPlaylistsSubscriber());
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package org.schabi.newpipe.local.subscription
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import org.schabi.newpipe.NewPipeDatabase
|
import org.schabi.newpipe.NewPipeDatabase
|
||||||
|
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionDAO
|
import org.schabi.newpipe.database.subscription.SubscriptionDAO
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||||
import org.schabi.newpipe.extractor.ListInfo
|
import org.schabi.newpipe.extractor.ListInfo
|
||||||
@@ -21,9 +23,28 @@ class SubscriptionManager(context: Context) {
|
|||||||
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
|
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
|
||||||
fun subscriptions() = subscriptionTable.all
|
fun subscriptions() = subscriptionTable.all
|
||||||
|
|
||||||
|
fun getSubscriptions(
|
||||||
|
currentGroupId: Long = FeedGroupEntity.GROUP_ALL_ID,
|
||||||
|
filterQuery: String = "",
|
||||||
|
showOnlyUngrouped: Boolean = false
|
||||||
|
): Flowable<List<SubscriptionEntity>> {
|
||||||
|
return when {
|
||||||
|
filterQuery.isNotEmpty() -> {
|
||||||
|
return if (showOnlyUngrouped) {
|
||||||
|
subscriptionTable.getSubscriptionsOnlyUngroupedFiltered(
|
||||||
|
currentGroupId, filterQuery)
|
||||||
|
} else {
|
||||||
|
subscriptionTable.getSubscriptionsFiltered(filterQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showOnlyUngrouped -> subscriptionTable.getSubscriptionsOnlyUngrouped(currentGroupId)
|
||||||
|
else -> subscriptionTable.all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
||||||
val listEntities = subscriptionTable.upsertAll(
|
val listEntities = subscriptionTable.upsertAll(
|
||||||
infoList.map { SubscriptionEntity.from(it) })
|
infoList.map { SubscriptionEntity.from(it) })
|
||||||
|
|
||||||
database.runInTransaction {
|
database.runInTransaction {
|
||||||
infoList.forEachIndexed { index, info ->
|
infoList.forEachIndexed { index, info ->
|
||||||
@@ -35,13 +56,13 @@ class SubscriptionManager(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
|
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)
|
||||||
.flatMapCompletable {
|
.flatMapCompletable {
|
||||||
Completable.fromRunnable {
|
Completable.fromRunnable {
|
||||||
it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
|
it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount)
|
||||||
subscriptionTable.update(it)
|
subscriptionTable.update(it)
|
||||||
feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
|
feedDatabaseManager.upsertAll(it.uid, info.relatedItems)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateFromInfo(subscriptionId: Long, info: ListInfo<StreamInfoItem>) {
|
fun updateFromInfo(subscriptionId: Long, info: ListInfo<StreamInfoItem>) {
|
||||||
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId)
|
val subscriptionEntity = subscriptionTable.getSubscription(subscriptionId)
|
||||||
@@ -57,8 +78,8 @@ class SubscriptionManager(context: Context) {
|
|||||||
|
|
||||||
fun deleteSubscription(serviceId: Int, url: String): Completable {
|
fun deleteSubscription(serviceId: Int, url: String): Completable {
|
||||||
return Completable.fromCallable { subscriptionTable.deleteSubscription(serviceId, url) }
|
return Completable.fromCallable { subscriptionTable.deleteSubscription(serviceId, url) }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) {
|
fun insertSubscription(subscriptionEntity: SubscriptionEntity, info: ChannelInfo) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,42 +9,56 @@ import io.reactivex.Completable
|
|||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
|
import io.reactivex.processors.BehaviorProcessor
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
|
||||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||||
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||||
|
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
|
||||||
|
|
||||||
class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModel() {
|
class FeedGroupDialogViewModel(
|
||||||
class Factory(val context: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModelProvider.Factory {
|
applicationContext: Context,
|
||||||
@Suppress("UNCHECKED_CAST")
|
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
initialQuery: String = "",
|
||||||
return FeedGroupDialogViewModel(context.applicationContext, groupId) as T
|
initialShowOnlyUngrouped: Boolean = false
|
||||||
}
|
) : ViewModel() {
|
||||||
}
|
|
||||||
|
|
||||||
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
|
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
|
||||||
private var subscriptionManager = SubscriptionManager(applicationContext)
|
private var subscriptionManager = SubscriptionManager(applicationContext)
|
||||||
|
|
||||||
|
private var filterSubscriptions = BehaviorProcessor.create<String>()
|
||||||
|
private var toggleShowOnlyUngrouped = BehaviorProcessor.create<Boolean>()
|
||||||
|
|
||||||
|
private var subscriptionsFlowable = Flowable
|
||||||
|
.combineLatest(
|
||||||
|
filterSubscriptions.startWith(initialQuery),
|
||||||
|
toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
|
||||||
|
BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
|
||||||
|
)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.switchMap { filter ->
|
||||||
|
subscriptionManager.getSubscriptions(groupId, filter.query, filter.showOnlyUngrouped)
|
||||||
|
}.map { list -> list.map { PickerSubscriptionItem(it) } }
|
||||||
|
|
||||||
private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>()
|
private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>()
|
||||||
private val mutableSubscriptionsLiveData = MutableLiveData<Pair<List<SubscriptionEntity>, Set<Long>>>()
|
private val mutableSubscriptionsLiveData = MutableLiveData<Pair<List<PickerSubscriptionItem>, Set<Long>>>()
|
||||||
private val mutableDialogEventLiveData = MutableLiveData<DialogEvent>()
|
private val mutableDialogEventLiveData = MutableLiveData<DialogEvent>()
|
||||||
val groupLiveData: LiveData<FeedGroupEntity> = mutableGroupLiveData
|
val groupLiveData: LiveData<FeedGroupEntity> = mutableGroupLiveData
|
||||||
val subscriptionsLiveData: LiveData<Pair<List<SubscriptionEntity>, Set<Long>>> = mutableSubscriptionsLiveData
|
val subscriptionsLiveData: LiveData<Pair<List<PickerSubscriptionItem>, Set<Long>>> = mutableSubscriptionsLiveData
|
||||||
val dialogEventLiveData: LiveData<DialogEvent> = mutableDialogEventLiveData
|
val dialogEventLiveData: LiveData<DialogEvent> = mutableDialogEventLiveData
|
||||||
|
|
||||||
private var actionProcessingDisposable: Disposable? = null
|
private var actionProcessingDisposable: Disposable? = null
|
||||||
|
|
||||||
private var feedGroupDisposable = feedDatabaseManager.getGroup(groupId)
|
private var feedGroupDisposable = feedDatabaseManager.getGroup(groupId)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe(mutableGroupLiveData::postValue)
|
.subscribe(mutableGroupLiveData::postValue)
|
||||||
|
|
||||||
private var subscriptionsDisposable = Flowable
|
private var subscriptionsDisposable = Flowable
|
||||||
.combineLatest(subscriptionManager.subscriptions(), feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
.combineLatest(subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
||||||
BiFunction { t1: List<SubscriptionEntity>, t2: List<Long> -> t1 to t2.toSet() })
|
BiFunction { t1: List<PickerSubscriptionItem>, t2: List<Long> -> t1 to t2.toSet() })
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe(mutableSubscriptionsLiveData::postValue)
|
.subscribe(mutableSubscriptionsLiveData::postValue)
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
@@ -55,14 +69,14 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
|
|||||||
|
|
||||||
fun createGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>) {
|
fun createGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>) {
|
||||||
doAction(feedDatabaseManager.createGroup(name, selectedIcon)
|
doAction(feedDatabaseManager.createGroup(name, selectedIcon)
|
||||||
.flatMapCompletable {
|
.flatMapCompletable {
|
||||||
feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
|
feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>, sortOrder: Long) {
|
fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>, sortOrder: Long) {
|
||||||
doAction(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
|
doAction(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
|
||||||
.andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
|
.andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteGroup() {
|
fun deleteGroup() {
|
||||||
@@ -74,13 +88,40 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
|
|||||||
mutableDialogEventLiveData.value = DialogEvent.ProcessingEvent
|
mutableDialogEventLiveData.value = DialogEvent.ProcessingEvent
|
||||||
|
|
||||||
actionProcessingDisposable = completable
|
actionProcessingDisposable = completable
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
|
.subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun filterSubscriptionsBy(query: String) {
|
||||||
|
filterSubscriptions.onNext(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSubscriptionsFilter() {
|
||||||
|
filterSubscriptions.onNext("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleShowOnlyUngrouped(showOnlyUngrouped: Boolean) {
|
||||||
|
toggleShowOnlyUngrouped.onNext(showOnlyUngrouped)
|
||||||
|
}
|
||||||
|
|
||||||
sealed class DialogEvent {
|
sealed class DialogEvent {
|
||||||
object ProcessingEvent : DialogEvent()
|
object ProcessingEvent : DialogEvent()
|
||||||
object SuccessEvent : DialogEvent()
|
object SuccessEvent : DialogEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Filter(val query: String, val showOnlyUngrouped: Boolean)
|
||||||
|
|
||||||
|
class Factory(
|
||||||
|
private val context: Context,
|
||||||
|
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
|
||||||
|
private val initialQuery: String = "",
|
||||||
|
private val initialShowOnlyUngrouped: Boolean = false
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
return FeedGroupDialogViewModel(context.applicationContext,
|
||||||
|
groupId, initialQuery, initialShowOnlyUngrouped) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ import org.schabi.newpipe.R
|
|||||||
class EmptyPlaceholderItem : Item() {
|
class EmptyPlaceholderItem : Item() {
|
||||||
override fun getLayout(): Int = R.layout.list_empty_view
|
override fun getLayout(): Int = R.layout.list_empty_view
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {}
|
override fun bind(viewHolder: GroupieViewHolder, position: Int) {}
|
||||||
|
override fun getSpanSize(spanCount: Int, position: Int): Int = spanCount
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,28 @@
|
|||||||
package org.schabi.newpipe.local.subscription.item
|
package org.schabi.newpipe.local.subscription.item
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader
|
import com.nostra13.universalimageloader.core.ImageLoader
|
||||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||||
import com.xwray.groupie.kotlinandroidextensions.Item
|
import com.xwray.groupie.kotlinandroidextensions.Item
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.selected_highlight
|
import kotlinx.android.synthetic.main.picker_subscription_item.*
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.thumbnail_view
|
import kotlinx.android.synthetic.main.picker_subscription_item.view.*
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.title_view
|
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||||
import org.schabi.newpipe.util.AnimationUtils
|
import org.schabi.newpipe.util.AnimationUtils
|
||||||
import org.schabi.newpipe.util.AnimationUtils.animateView
|
import org.schabi.newpipe.util.AnimationUtils.animateView
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants
|
import org.schabi.newpipe.util.ImageDisplayConstants
|
||||||
|
|
||||||
data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, var isSelected: Boolean = false) : Item() {
|
data class PickerSubscriptionItem(
|
||||||
companion object {
|
val subscriptionEntity: SubscriptionEntity,
|
||||||
const val UPDATE_SELECTED = 123
|
var isSelected: Boolean = false
|
||||||
|
) : Item() {
|
||||||
val IMAGE_LOADING_OPTIONS: DisplayImageOptions = ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS
|
override fun getId(): Long = subscriptionEntity.uid
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLayout(): Int = R.layout.picker_subscription_item
|
override fun getLayout(): Int = R.layout.picker_subscription_item
|
||||||
|
override fun getSpanSize(spanCount: Int, position: Int): Int = 1
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList<Any>) {
|
|
||||||
if (payloads.contains(UPDATE_SELECTED)) {
|
|
||||||
animateView(viewHolder.selected_highlight, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
super.bind(viewHolder, position, payloads)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||||
ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl, viewHolder.thumbnail_view, IMAGE_LOADING_OPTIONS)
|
ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl,
|
||||||
|
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
|
||||||
|
|
||||||
viewHolder.title_view.text = subscriptionEntity.name
|
viewHolder.title_view.text = subscriptionEntity.name
|
||||||
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
|
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||||
@@ -47,7 +36,9 @@ data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, va
|
|||||||
viewHolder.selected_highlight.alpha = 1F
|
viewHolder.selected_highlight.alpha = 1F
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getId(): Long {
|
fun updateSelected(containerView: View, isSelected: Boolean) {
|
||||||
return subscriptionEntity.uid
|
this.isSelected = isSelected
|
||||||
|
animateView(containerView.selected_highlight,
|
||||||
|
AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ import org.schabi.newpipe.player.helper.LoadController;
|
|||||||
import org.schabi.newpipe.player.helper.MediaSessionManager;
|
import org.schabi.newpipe.player.helper.MediaSessionManager;
|
||||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.mediasource.FailedMediaSource;
|
|
||||||
import org.schabi.newpipe.player.playback.BasePlayerMediaSession;
|
import org.schabi.newpipe.player.playback.BasePlayerMediaSession;
|
||||||
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
||||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
@@ -77,7 +76,6 @@ import org.schabi.newpipe.util.ImageDisplayConstants;
|
|||||||
import org.schabi.newpipe.util.SerializedCache;
|
import org.schabi.newpipe.util.SerializedCache;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
@@ -217,7 +215,7 @@ public abstract class BasePlayer implements
|
|||||||
|
|
||||||
final TrackSelection.Factory trackSelectionFactory = PlayerHelper
|
final TrackSelection.Factory trackSelectionFactory = PlayerHelper
|
||||||
.getQualitySelector(context);
|
.getQualitySelector(context);
|
||||||
this.trackSelector = new CustomTrackSelector(trackSelectionFactory);
|
this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory);
|
||||||
|
|
||||||
this.loadControl = new LoadController();
|
this.loadControl = new LoadController();
|
||||||
this.renderFactory = new DefaultRenderersFactory(context);
|
this.renderFactory = new DefaultRenderersFactory(context);
|
||||||
@@ -333,13 +331,12 @@ public abstract class BasePlayer implements
|
|||||||
final SharedPreferences preferences =
|
final SharedPreferences preferences =
|
||||||
PreferenceManager.getDefaultSharedPreferences(context);
|
PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
|
||||||
final float speed = preferences
|
final float speed = preferences.getFloat(
|
||||||
.getFloat(context.getString(R.string.playback_speed_key), getPlaybackSpeed());
|
context.getString(R.string.playback_speed_key), getPlaybackSpeed());
|
||||||
final float pitch = preferences.getFloat(context.getString(R.string.playback_pitch_key),
|
final float pitch = preferences.getFloat(
|
||||||
getPlaybackPitch());
|
context.getString(R.string.playback_pitch_key), getPlaybackPitch());
|
||||||
final boolean skipSilence = preferences
|
final boolean skipSilence = preferences.getBoolean(
|
||||||
.getBoolean(context.getString(R.string.playback_skip_silence_key),
|
context.getString(R.string.playback_skip_silence_key), getPlaybackSkipSilence());
|
||||||
getPlaybackSkipSilence());
|
|
||||||
return new PlaybackParameters(speed, pitch, skipSilence);
|
return new PlaybackParameters(speed, pitch, skipSilence);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,16 +832,8 @@ public abstract class BasePlayer implements
|
|||||||
final Throwable cause = error.getCause();
|
final Throwable cause = error.getCause();
|
||||||
if (error instanceof BehindLiveWindowException) {
|
if (error instanceof BehindLiveWindowException) {
|
||||||
reload();
|
reload();
|
||||||
} else if (cause instanceof UnknownHostException) {
|
|
||||||
playQueue.error(/*isNetworkProblem=*/true);
|
|
||||||
} else if (isCurrentWindowValid()) {
|
|
||||||
playQueue.error(/*isTransitioningToBadStream=*/true);
|
|
||||||
} else if (cause instanceof FailedMediaSource.MediaSourceResolutionException) {
|
|
||||||
playQueue.error(/*recoverableWithNoAvailableStream=*/false);
|
|
||||||
} else if (cause instanceof FailedMediaSource.StreamInfoLoadException) {
|
|
||||||
playQueue.error(/*recoverableIfLoadFailsWhenNetworkIsFine=*/false);
|
|
||||||
} else {
|
} else {
|
||||||
playQueue.error(/*noIdeaWhatHappenedAndLetUserChooseWhatToDo=*/true);
|
playQueue.error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,6 +1120,7 @@ public abstract class BasePlayer implements
|
|||||||
Log.d(TAG, "onFastRewind() called");
|
Log.d(TAG, "onFastRewind() called");
|
||||||
}
|
}
|
||||||
seekBy(-getSeekDuration());
|
seekBy(-getSeekDuration());
|
||||||
|
triggerProgressUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFastForward() {
|
public void onFastForward() {
|
||||||
@@ -1138,6 +1128,7 @@ public abstract class BasePlayer implements
|
|||||||
Log.d(TAG, "onFastForward() called");
|
Log.d(TAG, "onFastForward() called");
|
||||||
}
|
}
|
||||||
seekBy(getSeekDuration());
|
seekBy(getSeekDuration());
|
||||||
|
triggerProgressUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSeekDuration() {
|
private int getSeekDuration() {
|
||||||
@@ -1479,10 +1470,21 @@ public abstract class BasePlayer implements
|
|||||||
return parameters == null ? PlaybackParameters.DEFAULT : parameters;
|
return parameters == null ? PlaybackParameters.DEFAULT : parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the playback parameters of the player, and also saves them to shared preferences.
|
||||||
|
* Speed and pitch are rounded up to 2 decimal places before being used or saved.
|
||||||
|
* @param speed the playback speed, will be rounded to up to 2 decimal places
|
||||||
|
* @param pitch the playback pitch, will be rounded to up to 2 decimal places
|
||||||
|
* @param skipSilence skip silence during playback
|
||||||
|
*/
|
||||||
public void setPlaybackParameters(final float speed, final float pitch,
|
public void setPlaybackParameters(final float speed, final float pitch,
|
||||||
final boolean skipSilence) {
|
final boolean skipSilence) {
|
||||||
savePlaybackParametersToPreferences(speed, pitch, skipSilence);
|
final float roundedSpeed = Math.round(speed * 100.0f) / 100.0f;
|
||||||
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch, skipSilence));
|
final float roundedPitch = Math.round(pitch * 100.0f) / 100.0f;
|
||||||
|
|
||||||
|
savePlaybackParametersToPreferences(roundedSpeed, roundedPitch, skipSilence);
|
||||||
|
simpleExoPlayer.setPlaybackParameters(
|
||||||
|
new PlaybackParameters(roundedSpeed, roundedPitch, skipSilence));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void savePlaybackParametersToPreferences(final float speed, final float pitch,
|
private void savePlaybackParametersToPreferences(final float speed, final float pitch,
|
||||||
|
|||||||
@@ -166,9 +166,6 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
initPopup();
|
initPopup();
|
||||||
initPopupCloseOverlay();
|
initPopupCloseOverlay();
|
||||||
}
|
}
|
||||||
if (!playerImpl.isPlaying()) {
|
|
||||||
playerImpl.getPlayer().setPlayWhenReady(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
playerImpl.handleIntent(intent);
|
playerImpl.handleIntent(intent);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import org.schabi.newpipe.util.ThemeHelper;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatPitch;
|
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
|
||||||
@@ -84,14 +83,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
|
|
||||||
private ImageButton repeatButton;
|
private ImageButton repeatButton;
|
||||||
private ImageButton backwardButton;
|
private ImageButton backwardButton;
|
||||||
|
private ImageButton fastRewindButton;
|
||||||
private ImageButton playPauseButton;
|
private ImageButton playPauseButton;
|
||||||
|
private ImageButton fastForwardButton;
|
||||||
private ImageButton forwardButton;
|
private ImageButton forwardButton;
|
||||||
private ImageButton shuffleButton;
|
private ImageButton shuffleButton;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
private TextView playbackSpeedButton;
|
|
||||||
private TextView playbackPitchButton;
|
|
||||||
|
|
||||||
private Menu menu;
|
private Menu menu;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -166,6 +164,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
case R.id.action_append_playlist:
|
case R.id.action_append_playlist:
|
||||||
appendAllToPlaylist();
|
appendAllToPlaylist();
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_playback_speed:
|
||||||
|
openPlaybackParameterDialog();
|
||||||
|
return true;
|
||||||
case R.id.action_mute:
|
case R.id.action_mute:
|
||||||
player.onMuteUnmuteButtonClicked();
|
player.onMuteUnmuteButtonClicked();
|
||||||
return true;
|
return true;
|
||||||
@@ -310,20 +311,20 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
private void buildControls() {
|
private void buildControls() {
|
||||||
repeatButton = rootView.findViewById(R.id.control_repeat);
|
repeatButton = rootView.findViewById(R.id.control_repeat);
|
||||||
backwardButton = rootView.findViewById(R.id.control_backward);
|
backwardButton = rootView.findViewById(R.id.control_backward);
|
||||||
|
fastRewindButton = rootView.findViewById(R.id.control_fast_rewind);
|
||||||
playPauseButton = rootView.findViewById(R.id.control_play_pause);
|
playPauseButton = rootView.findViewById(R.id.control_play_pause);
|
||||||
|
fastForwardButton = rootView.findViewById(R.id.control_fast_forward);
|
||||||
forwardButton = rootView.findViewById(R.id.control_forward);
|
forwardButton = rootView.findViewById(R.id.control_forward);
|
||||||
shuffleButton = rootView.findViewById(R.id.control_shuffle);
|
shuffleButton = rootView.findViewById(R.id.control_shuffle);
|
||||||
playbackSpeedButton = rootView.findViewById(R.id.control_playback_speed);
|
|
||||||
playbackPitchButton = rootView.findViewById(R.id.control_playback_pitch);
|
|
||||||
progressBar = rootView.findViewById(R.id.control_progress_bar);
|
progressBar = rootView.findViewById(R.id.control_progress_bar);
|
||||||
|
|
||||||
repeatButton.setOnClickListener(this);
|
repeatButton.setOnClickListener(this);
|
||||||
backwardButton.setOnClickListener(this);
|
backwardButton.setOnClickListener(this);
|
||||||
|
fastRewindButton.setOnClickListener(this);
|
||||||
playPauseButton.setOnClickListener(this);
|
playPauseButton.setOnClickListener(this);
|
||||||
|
fastForwardButton.setOnClickListener(this);
|
||||||
forwardButton.setOnClickListener(this);
|
forwardButton.setOnClickListener(this);
|
||||||
shuffleButton.setOnClickListener(this);
|
shuffleButton.setOnClickListener(this);
|
||||||
playbackSpeedButton.setOnClickListener(this);
|
|
||||||
playbackPitchButton.setOnClickListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
|
private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
|
||||||
@@ -473,16 +474,16 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
player.onRepeatClicked();
|
player.onRepeatClicked();
|
||||||
} else if (view.getId() == backwardButton.getId()) {
|
} else if (view.getId() == backwardButton.getId()) {
|
||||||
player.onPlayPrevious();
|
player.onPlayPrevious();
|
||||||
|
} else if (view.getId() == fastRewindButton.getId()) {
|
||||||
|
player.onFastRewind();
|
||||||
} else if (view.getId() == playPauseButton.getId()) {
|
} else if (view.getId() == playPauseButton.getId()) {
|
||||||
player.onPlayPause();
|
player.onPlayPause();
|
||||||
|
} else if (view.getId() == fastForwardButton.getId()) {
|
||||||
|
player.onFastForward();
|
||||||
} else if (view.getId() == forwardButton.getId()) {
|
} else if (view.getId() == forwardButton.getId()) {
|
||||||
player.onPlayNext();
|
player.onPlayNext();
|
||||||
} else if (view.getId() == shuffleButton.getId()) {
|
} else if (view.getId() == shuffleButton.getId()) {
|
||||||
player.onShuffleClicked();
|
player.onShuffleClicked();
|
||||||
} else if (view.getId() == playbackSpeedButton.getId()) {
|
|
||||||
openPlaybackParameterDialog();
|
|
||||||
} else if (view.getId() == playbackPitchButton.getId()) {
|
|
||||||
openPlaybackParameterDialog();
|
|
||||||
} else if (view.getId() == metadata.getId()) {
|
} else if (view.getId() == metadata.getId()) {
|
||||||
scrollToSelected();
|
scrollToSelected();
|
||||||
} else if (view.getId() == progressLiveSync.getId()) {
|
} else if (view.getId() == progressLiveSync.getId()) {
|
||||||
@@ -690,8 +691,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||||||
|
|
||||||
private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
|
private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
|
||||||
if (parameters != null) {
|
if (parameters != null) {
|
||||||
playbackSpeedButton.setText(formatSpeed(parameters.speed));
|
if (menu != null && player != null) {
|
||||||
playbackPitchButton.setText(formatPitch(parameters.pitch));
|
final MenuItem item = menu.findItem(R.id.action_playback_speed);
|
||||||
|
item.setTitle(formatSpeed(parameters.speed));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.schabi.newpipe.player.playback;
|
package org.schabi.newpipe.player.playback;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
@@ -26,8 +27,9 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||||||
public class CustomTrackSelector extends DefaultTrackSelector {
|
public class CustomTrackSelector extends DefaultTrackSelector {
|
||||||
private String preferredTextLanguage;
|
private String preferredTextLanguage;
|
||||||
|
|
||||||
public CustomTrackSelector(final TrackSelection.Factory adaptiveTrackSelectionFactory) {
|
public CustomTrackSelector(final Context context,
|
||||||
super(adaptiveTrackSelectionFactory);
|
final TrackSelection.Factory adaptiveTrackSelectionFactory) {
|
||||||
|
super(context, adaptiveTrackSelectionFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean formatHasLanguage(final Format format, final String language) {
|
private static boolean formatHasLanguage(final Format format, final String language) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.util.Log;
|
|||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.ListInfo;
|
import org.schabi.newpipe.extractor.ListInfo;
|
||||||
|
import org.schabi.newpipe.extractor.Page;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -21,7 +22,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||||||
|
|
||||||
final int serviceId;
|
final int serviceId;
|
||||||
final String baseUrl;
|
final String baseUrl;
|
||||||
String nextUrl;
|
Page nextPage;
|
||||||
|
|
||||||
private transient Disposable fetchReactor;
|
private transient Disposable fetchReactor;
|
||||||
|
|
||||||
@@ -29,16 +30,16 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||||||
this(item.getServiceId(), item.getUrl(), null, Collections.emptyList(), 0);
|
this(item.getServiceId(), item.getUrl(), null, Collections.emptyList(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractInfoPlayQueue(final int serviceId, final String url, final String nextPageUrl,
|
AbstractInfoPlayQueue(final int serviceId, final String url, final Page nextPage,
|
||||||
final List<StreamInfoItem> streams, final int index) {
|
final List<StreamInfoItem> streams, final int index) {
|
||||||
super(index, extractListItems(streams));
|
super(index, extractListItems(streams));
|
||||||
|
|
||||||
this.baseUrl = url;
|
this.baseUrl = url;
|
||||||
this.nextUrl = nextPageUrl;
|
this.nextPage = nextPage;
|
||||||
this.serviceId = serviceId;
|
this.serviceId = serviceId;
|
||||||
|
|
||||||
this.isInitial = streams.isEmpty();
|
this.isInitial = streams.isEmpty();
|
||||||
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
this.isComplete = !isInitial && !Page.isValid(nextPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getTag();
|
protected abstract String getTag();
|
||||||
@@ -66,7 +67,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||||||
if (!result.hasNextPage()) {
|
if (!result.hasNextPage()) {
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
}
|
}
|
||||||
nextUrl = result.getNextPageUrl();
|
nextPage = result.getNextPage();
|
||||||
|
|
||||||
append(extractListItems(result.getRelatedItems()));
|
append(extractListItems(result.getRelatedItems()));
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||||||
if (!result.hasNextPage()) {
|
if (!result.hasNextPage()) {
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
}
|
}
|
||||||
nextUrl = result.getNextPageUrl();
|
nextPage = result.getNextPage();
|
||||||
|
|
||||||
append(extractListItems(result.getItems()));
|
append(extractListItems(result.getItems()));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.schabi.newpipe.player.playqueue;
|
package org.schabi.newpipe.player.playqueue;
|
||||||
|
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.Page;
|
||||||
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;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
@@ -17,15 +18,15 @@ public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ChannelPlayQueue(final ChannelInfo info) {
|
public ChannelPlayQueue(final ChannelInfo info) {
|
||||||
this(info.getServiceId(), info.getUrl(), info.getNextPageUrl(), info.getRelatedItems(), 0);
|
this(info.getServiceId(), info.getUrl(), info.getNextPage(), info.getRelatedItems(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelPlayQueue(final int serviceId,
|
public ChannelPlayQueue(final int serviceId,
|
||||||
final String url,
|
final String url,
|
||||||
final String nextPageUrl,
|
final Page nextPage,
|
||||||
final List<StreamInfoItem> streams,
|
final List<StreamInfoItem> streams,
|
||||||
final int index) {
|
final int index) {
|
||||||
super(serviceId, url, nextPageUrl, streams, index);
|
super(serviceId, url, nextPage, streams, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -41,7 +42,7 @@ public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, C
|
|||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(getHeadListObserver());
|
.subscribe(getHeadListObserver());
|
||||||
} else {
|
} else {
|
||||||
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
|
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextPage)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(getNextPageObserver());
|
.subscribe(getNextPageObserver());
|
||||||
|
|||||||
@@ -305,25 +305,16 @@ public abstract class PlayQueue implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report an exception for the item at the current index in order and the course of action:
|
* Report an exception for the item at the current index in order and skip to the next one
|
||||||
* if the error can be skipped or the current item should be removed.
|
|
||||||
* <p>
|
* <p>
|
||||||
* This is done as a separate event as the underlying manager may have
|
* This is done as a separate event as the underlying manager may have
|
||||||
* different implementation regarding exceptions.
|
* different implementation regarding exceptions.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* @param skippable whether the error could be skipped
|
|
||||||
*/
|
*/
|
||||||
public synchronized void error(final boolean skippable) {
|
public synchronized void error() {
|
||||||
final int index = getIndex();
|
final int oldIndex = getIndex();
|
||||||
|
queueIndex.incrementAndGet();
|
||||||
if (skippable) {
|
broadcast(new ErrorEvent(oldIndex, getIndex()));
|
||||||
queueIndex.incrementAndGet();
|
|
||||||
} else {
|
|
||||||
removeInternal(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast(new ErrorEvent(index, getIndex(), skippable));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void removeInternal(final int removeIndex) {
|
private synchronized void removeInternal(final int removeIndex) {
|
||||||
|
|||||||
@@ -115,9 +115,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
|||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
final ErrorEvent errorEvent = (ErrorEvent) message;
|
final ErrorEvent errorEvent = (ErrorEvent) message;
|
||||||
if (!errorEvent.isSkippable()) {
|
|
||||||
notifyItemRemoved(errorEvent.getErrorIndex());
|
|
||||||
}
|
|
||||||
notifyItemChanged(errorEvent.getErrorIndex());
|
notifyItemChanged(errorEvent.getErrorIndex());
|
||||||
notifyItemChanged(errorEvent.getQueueIndex());
|
notifyItemChanged(errorEvent.getQueueIndex());
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.schabi.newpipe.player.playqueue;
|
package org.schabi.newpipe.player.playqueue;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.Page;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
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;
|
||||||
@@ -16,15 +17,15 @@ public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PlaylistPlayQueue(final PlaylistInfo info) {
|
public PlaylistPlayQueue(final PlaylistInfo info) {
|
||||||
this(info.getServiceId(), info.getUrl(), info.getNextPageUrl(), info.getRelatedItems(), 0);
|
this(info.getServiceId(), info.getUrl(), info.getNextPage(), info.getRelatedItems(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlaylistPlayQueue(final int serviceId,
|
public PlaylistPlayQueue(final int serviceId,
|
||||||
final String url,
|
final String url,
|
||||||
final String nextPageUrl,
|
final Page nextPage,
|
||||||
final List<StreamInfoItem> streams,
|
final List<StreamInfoItem> streams,
|
||||||
final int index) {
|
final int index) {
|
||||||
super(serviceId, url, nextPageUrl, streams, index);
|
super(serviceId, url, nextPage, streams, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -40,7 +41,7 @@ public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo,
|
|||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(getHeadListObserver());
|
.subscribe(getHeadListObserver());
|
||||||
} else {
|
} else {
|
||||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextPage)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(getNextPageObserver());
|
.subscribe(getNextPageObserver());
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ package org.schabi.newpipe.player.playqueue.events;
|
|||||||
public class ErrorEvent implements PlayQueueEvent {
|
public class ErrorEvent implements PlayQueueEvent {
|
||||||
private final int errorIndex;
|
private final int errorIndex;
|
||||||
private final int queueIndex;
|
private final int queueIndex;
|
||||||
private final boolean skippable;
|
|
||||||
|
|
||||||
public ErrorEvent(final int errorIndex, final int queueIndex, final boolean skippable) {
|
public ErrorEvent(final int errorIndex, final int queueIndex) {
|
||||||
this.errorIndex = errorIndex;
|
this.errorIndex = errorIndex;
|
||||||
this.queueIndex = queueIndex;
|
this.queueIndex = queueIndex;
|
||||||
this.skippable = skippable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -23,8 +21,4 @@ public class ErrorEvent implements PlayQueueEvent {
|
|||||||
public int getQueueIndex() {
|
public int getQueueIndex() {
|
||||||
return queueIndex;
|
return queueIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSkippable() {
|
|
||||||
return skippable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user