Update most dependencies
This commit is contained in:
parent
c88b4032ef
commit
f0ca916432
87 changed files with 853 additions and 712 deletions
|
@ -91,18 +91,18 @@ ext {
|
|||
icepickVersion = '3.2.0'
|
||||
checkstyleVersion = '8.36.2'
|
||||
stethoVersion = '1.5.1'
|
||||
leakCanaryVersion = '2.2'
|
||||
leakCanaryVersion = '2.5'
|
||||
exoPlayerVersion = '2.11.8'
|
||||
androidxLifecycleVersion = '2.2.0'
|
||||
androidxRoomVersion = '2.2.5'
|
||||
groupieVersion = '2.8.0'
|
||||
markwonVersion = '4.3.1'
|
||||
androidxRoomVersion = '2.3.0-alpha03'
|
||||
groupieVersion = '2.8.1'
|
||||
markwonVersion = '4.6.0'
|
||||
googleAutoServiceVersion = '1.0-rc7'
|
||||
}
|
||||
|
||||
configurations {
|
||||
checkstyle
|
||||
// ktlint
|
||||
ktlint
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
|
@ -130,24 +130,31 @@ task runCheckstyle(type: Checkstyle) {
|
|||
}
|
||||
}
|
||||
|
||||
//task runKtlint(type: JavaExec) {
|
||||
// main = "com.pinterest.ktlint.Main"
|
||||
// classpath = configurations.ktlint
|
||||
// args "src/**/*.kt"
|
||||
//}
|
||||
//
|
||||
//task formatKtlint(type: JavaExec) {
|
||||
// main = "com.pinterest.ktlint.Main"
|
||||
// classpath = configurations.ktlint
|
||||
// args "-F", "src/**/*.kt"
|
||||
//}
|
||||
def outputDir = "${project.buildDir}/reports/ktlint/"
|
||||
def inputFiles = project.fileTree(dir: "src", include: "**/*.kt")
|
||||
|
||||
task runKtlint(type: JavaExec) {
|
||||
inputs.files(inputFiles)
|
||||
outputs.dir(outputDir)
|
||||
main = "com.pinterest.ktlint.Main"
|
||||
classpath = configurations.ktlint
|
||||
args "src/**/*.kt"
|
||||
}
|
||||
|
||||
task formatKtlint(type: JavaExec) {
|
||||
inputs.files(inputFiles)
|
||||
outputs.dir(outputDir)
|
||||
main = "com.pinterest.ktlint.Main"
|
||||
classpath = configurations.ktlint
|
||||
args "-F", "src/**/*.kt"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
preDebugBuild.dependsOn runCheckstyle //, runKtlint
|
||||
preDebugBuild.dependsOn runCheckstyle, runKtlint
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
||||
|
@ -155,22 +162,23 @@ dependencies {
|
|||
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
||||
|
||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||
// ktlint "com.pinterest:ktlint:0.35.0"
|
||||
ktlint "com.pinterest:ktlint:0.39.0"
|
||||
|
||||
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
||||
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
|
||||
|
||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
|
||||
implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
||||
implementation "com.squareup.leakcanary:plumber-android:${leakCanaryVersion}"
|
||||
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
testImplementation 'org.mockito:mockito-core:3.3.3'
|
||||
testImplementation 'org.mockito:mockito-core:3.5.13'
|
||||
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.1"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
||||
androidTestImplementation "androidx.room:room-testing:${androidxRoomVersion}"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0", {
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0", {
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
|
||||
|
@ -181,28 +189,31 @@ dependencies {
|
|||
|
||||
implementation "org.jsoup:jsoup:1.13.1"
|
||||
|
||||
//noinspection GradleDependency --> do not update okhttp to keep supporting Android 4.4 users
|
||||
implementation "com.squareup.okhttp3:okhttp:3.12.12"
|
||||
|
||||
implementation "com.google.android.exoplayer:exoplayer:${exoPlayerVersion}"
|
||||
implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerVersion}"
|
||||
|
||||
implementation "com.google.android.material:material:1.1.0"
|
||||
implementation "com.google.android.material:material:1.2.1"
|
||||
|
||||
compileOnly "com.google.auto.service:auto-service-annotations:${googleAutoServiceVersion}"
|
||||
kapt "com.google.auto.service:auto-service:${googleAutoServiceVersion}"
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
|
||||
|
||||
implementation "androidx.room:room-runtime:${androidxRoomVersion}"
|
||||
implementation "androidx.room:room-rxjava2:${androidxRoomVersion}"
|
||||
implementation "androidx.room:room-rxjava3:${androidxRoomVersion}"
|
||||
kapt "androidx.room:room-compiler:${androidxRoomVersion}"
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
|
@ -218,11 +229,11 @@ dependencies {
|
|||
|
||||
implementation "com.nononsenseapps:filepicker:4.2.1"
|
||||
|
||||
implementation "ch.acra:acra-core:5.5.0"
|
||||
implementation "ch.acra:acra-core:5.7.0"
|
||||
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||
implementation "com.jakewharton.rxbinding2:rxbinding:2.2.0"
|
||||
implementation "io.reactivex.rxjava3:rxjava:3.0.7"
|
||||
implementation "io.reactivex.rxjava3:rxandroid:3.0.0"
|
||||
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
|
||||
|
||||
implementation "org.ocpsoft.prettytime:prettytime:4.0.6.Final"
|
||||
}
|
||||
|
|
|
@ -31,49 +31,62 @@ class AppDatabaseTest {
|
|||
}
|
||||
|
||||
@get:Rule
|
||||
val testHelper = MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory())
|
||||
val testHelper = MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun migrateDatabaseFrom2to3() {
|
||||
val databaseInV2 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_2)
|
||||
|
||||
databaseInV2.run {
|
||||
insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SERVICE_ID)
|
||||
put("url", DEFAULT_URL)
|
||||
put("title", DEFAULT_TITLE)
|
||||
put("stream_type", DEFAULT_TYPE.name)
|
||||
put("duration", DEFAULT_DURATION)
|
||||
put("uploader", DEFAULT_UPLOADER_NAME)
|
||||
put("thumbnail_url", DEFAULT_THUMBNAIL)
|
||||
})
|
||||
insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SECOND_SERVICE_ID)
|
||||
put("url", DEFAULT_SECOND_URL)
|
||||
// put("title", null)
|
||||
// put("stream_type", null)
|
||||
// put("duration", null)
|
||||
// put("uploader", null)
|
||||
// put("thumbnail_url", null)
|
||||
})
|
||||
insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SERVICE_ID)
|
||||
// put("url", null)
|
||||
// put("title", null)
|
||||
// put("stream_type", null)
|
||||
// put("duration", null)
|
||||
// put("uploader", null)
|
||||
// put("thumbnail_url", null)
|
||||
})
|
||||
insert(
|
||||
"streams", SQLiteDatabase.CONFLICT_FAIL,
|
||||
ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SERVICE_ID)
|
||||
put("url", DEFAULT_URL)
|
||||
put("title", DEFAULT_TITLE)
|
||||
put("stream_type", DEFAULT_TYPE.name)
|
||||
put("duration", DEFAULT_DURATION)
|
||||
put("uploader", DEFAULT_UPLOADER_NAME)
|
||||
put("thumbnail_url", DEFAULT_THUMBNAIL)
|
||||
}
|
||||
)
|
||||
insert(
|
||||
"streams", SQLiteDatabase.CONFLICT_FAIL,
|
||||
ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SECOND_SERVICE_ID)
|
||||
put("url", DEFAULT_SECOND_URL)
|
||||
// put("title", null)
|
||||
// put("stream_type", null)
|
||||
// put("duration", null)
|
||||
// put("uploader", null)
|
||||
// put("thumbnail_url", null)
|
||||
}
|
||||
)
|
||||
insert(
|
||||
"streams", SQLiteDatabase.CONFLICT_FAIL,
|
||||
ContentValues().apply {
|
||||
// put("uid", null)
|
||||
put("service_id", DEFAULT_SERVICE_ID)
|
||||
// put("url", null)
|
||||
// put("title", null)
|
||||
// put("stream_type", null)
|
||||
// put("duration", null)
|
||||
// put("uploader", null)
|
||||
// put("thumbnail_url", null)
|
||||
}
|
||||
)
|
||||
close()
|
||||
}
|
||||
|
||||
testHelper.runMigrationsAndValidate(AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
|
||||
true, Migrations.MIGRATION_2_3)
|
||||
testHelper.runMigrationsAndValidate(
|
||||
AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
|
||||
true, Migrations.MIGRATION_2_3
|
||||
)
|
||||
|
||||
val migratedDatabaseV3 = getMigratedDatabase()
|
||||
val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst()
|
||||
|
@ -110,9 +123,11 @@ class AppDatabaseTest {
|
|||
}
|
||||
|
||||
private fun getMigratedDatabase(): AppDatabase {
|
||||
val database: AppDatabase = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
|
||||
AppDatabase::class.java, AppDatabase.DATABASE_NAME)
|
||||
.build()
|
||||
val database: AppDatabase = Room.databaseBuilder(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
AppDatabase::class.java, AppDatabase.DATABASE_NAME
|
||||
)
|
||||
.build()
|
||||
testHelper.closeWhenFinished(database)
|
||||
return database
|
||||
}
|
||||
|
|
|
@ -15,14 +15,22 @@ class DebugApp : App() {
|
|||
|
||||
// Give each object 10 seconds to be GC'ed, before LeakCanary gets nosy on it
|
||||
AppWatcher.config = AppWatcher.config.copy(watchDurationMillis = 10000)
|
||||
LeakCanary.config = LeakCanary.config.copy(dumpHeap = PreferenceManager
|
||||
.getDefaultSharedPreferences(this).getBoolean(getString(
|
||||
R.string.allow_heap_dumping_key), false))
|
||||
LeakCanary.config = LeakCanary.config.copy(
|
||||
dumpHeap = PreferenceManager
|
||||
.getDefaultSharedPreferences(this).getBoolean(
|
||||
getString(
|
||||
R.string.allow_heap_dumping_key
|
||||
),
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getDownloader(): Downloader {
|
||||
val downloader = DownloaderImpl.init(OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(StethoInterceptor()))
|
||||
val downloader = DownloaderImpl.init(
|
||||
OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(StethoInterceptor())
|
||||
)
|
||||
setCookiesToDownloader(downloader)
|
||||
return downloader
|
||||
}
|
||||
|
@ -36,7 +44,8 @@ class DebugApp : App() {
|
|||
|
||||
// Enable command line interface
|
||||
initializerBuilder.enableDumpapp(
|
||||
Stetho.defaultDumperPluginsProvider(applicationContext))
|
||||
Stetho.defaultDumperPluginsProvider(applicationContext)
|
||||
)
|
||||
|
||||
// Use the InitializerBuilder to generate an Initializer
|
||||
val initializer = initializerBuilder.build()
|
||||
|
@ -47,6 +56,6 @@ class DebugApp : App() {
|
|||
|
||||
override fun isDisposedRxExceptionsReported(): Boolean {
|
||||
return PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean(getString(R.string.allow_disposed_exceptions_key), false)
|
||||
.getBoolean(getString(R.string.allow_disposed_exceptions_key), false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,13 +38,13 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.exceptions.CompositeException;
|
||||
import io.reactivex.exceptions.MissingBackpressureException;
|
||||
import io.reactivex.exceptions.OnErrorNotImplementedException;
|
||||
import io.reactivex.exceptions.UndeliverableException;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.plugins.RxJavaPlugins;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.exceptions.CompositeException;
|
||||
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
||||
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
||||
import io.reactivex.rxjava3.exceptions.UndeliverableException;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
|
||||
|
||||
/*
|
||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||
|
|
|
@ -35,10 +35,10 @@ import java.security.cert.CertificateException;
|
|||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class CheckForNewAppVersion {
|
||||
private CheckForNewAppVersion() { }
|
||||
|
|
|
@ -66,13 +66,13 @@ import java.util.List;
|
|||
|
||||
import icepick.Icepick;
|
||||
import icepick.State;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.io.Serializable;
|
|||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
|
||||
/**
|
||||
* Fragment containing the software licenses.
|
||||
|
|
|
@ -16,11 +16,10 @@ import java.io.IOException;
|
|||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.Disposables;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||
|
||||
|
@ -55,7 +54,7 @@ public final class LicenseFragmentHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param context the Android context
|
||||
* @return String which is a CSS stylesheet according to the context's theme
|
||||
*/
|
||||
private static String getLicenseStylesheet(@NonNull final Context context) {
|
||||
|
@ -86,7 +85,7 @@ public final class LicenseFragmentHelper {
|
|||
|
||||
static Disposable showLicense(@Nullable final Context context, @NonNull final License license) {
|
||||
if (context == null) {
|
||||
return Disposables.empty();
|
||||
return Disposable.empty();
|
||||
}
|
||||
|
||||
return Observable.fromCallable(() -> getFormattedLicense(context, license))
|
||||
|
|
|
@ -9,7 +9,7 @@ import androidx.room.Update;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
@Dao
|
||||
public interface BasicDAO<Entity> {
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
package org.schabi.newpipe.database.feed.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.reactivex.Flowable
|
||||
import java.time.OffsetDateTime
|
||||
import androidx.room.*
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import org.schabi.newpipe.database.feed.model.FeedEntity
|
||||
import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@Dao
|
||||
abstract class FeedDAO {
|
||||
@Query("DELETE FROM feed")
|
||||
abstract fun deleteAll(): Int
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT s.* FROM streams s
|
||||
|
||||
INNER JOIN feed f
|
||||
|
@ -27,10 +23,12 @@ abstract class FeedDAO {
|
|||
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
|
||||
|
||||
LIMIT 500
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getAllStreams(): Flowable<List<StreamEntity>>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT s.* FROM streams s
|
||||
|
||||
INNER JOIN feed f
|
||||
|
@ -46,10 +44,12 @@ abstract class FeedDAO {
|
|||
|
||||
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
|
||||
LIMIT 500
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getAllStreamsFromGroup(groupId: Long): Flowable<List<StreamEntity>>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM feed WHERE
|
||||
|
||||
feed.stream_id IN (
|
||||
|
@ -60,10 +60,12 @@ abstract class FeedDAO {
|
|||
|
||||
WHERE s.upload_date < :offsetDateTime
|
||||
)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime)
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM feed
|
||||
|
||||
WHERE feed.subscription_id = :subscriptionId
|
||||
|
@ -76,7 +78,8 @@ abstract class FeedDAO {
|
|||
|
||||
WHERE s.stream_type = "LIVE_STREAM" OR s.stream_type = "AUDIO_LIVE_STREAM"
|
||||
)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun unlinkOldLivestreams(subscriptionId: Long)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
|
@ -100,12 +103,14 @@ abstract class FeedDAO {
|
|||
}
|
||||
}
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT MIN(lu.last_updated) FROM feed_last_updated lu
|
||||
|
||||
INNER JOIN feed_group_subscription_join fgs
|
||||
ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable<List<OffsetDateTime>>
|
||||
|
||||
@Query("SELECT MIN(last_updated) FROM feed_last_updated")
|
||||
|
@ -114,7 +119,8 @@ abstract class FeedDAO {
|
|||
@Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL")
|
||||
abstract fun notLoadedCount(): Flowable<Long>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT COUNT(*) FROM subscriptions s
|
||||
|
||||
INNER JOIN feed_group_subscription_join fgs
|
||||
|
@ -124,20 +130,24 @@ abstract class FeedDAO {
|
|||
ON s.uid = lu.subscription_id
|
||||
|
||||
WHERE lu.last_updated IS NULL
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun notLoadedCountForGroup(groupId: Long): Flowable<Long>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT s.* FROM subscriptions s
|
||||
|
||||
LEFT JOIN feed_last_updated lu
|
||||
ON s.uid = lu.subscription_id
|
||||
|
||||
WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flowable<List<SubscriptionEntity>>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT s.* FROM subscriptions s
|
||||
|
||||
INNER JOIN feed_group_subscription_join fgs
|
||||
|
@ -147,6 +157,7 @@ abstract class FeedDAO {
|
|||
ON s.uid = lu.subscription_id
|
||||
|
||||
WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flowable<List<SubscriptionEntity>>
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import androidx.room.OnConflictStrategy
|
|||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity
|
||||
|
||||
|
|
|
@ -10,21 +10,24 @@ import org.schabi.newpipe.database.feed.model.FeedEntity.Companion.SUBSCRIPTION_
|
|||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
|
||||
@Entity(tableName = FEED_TABLE,
|
||||
primaryKeys = [STREAM_ID, SUBSCRIPTION_ID],
|
||||
indices = [Index(SUBSCRIPTION_ID)],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = StreamEntity::class,
|
||||
parentColumns = [StreamEntity.STREAM_ID],
|
||||
childColumns = [STREAM_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true),
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true)
|
||||
]
|
||||
@Entity(
|
||||
tableName = FEED_TABLE,
|
||||
primaryKeys = [STREAM_ID, SUBSCRIPTION_ID],
|
||||
indices = [Index(SUBSCRIPTION_ID)],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = StreamEntity::class,
|
||||
parentColumns = [StreamEntity.STREAM_ID],
|
||||
childColumns = [STREAM_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
|
||||
),
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
|
||||
)
|
||||
]
|
||||
)
|
||||
data class FeedEntity(
|
||||
@ColumnInfo(name = STREAM_ID)
|
||||
|
|
|
@ -9,8 +9,8 @@ import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.SORT_ORD
|
|||
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
||||
|
||||
@Entity(
|
||||
tableName = FEED_GROUP_TABLE,
|
||||
indices = [Index(SORT_ORDER)]
|
||||
tableName = FEED_GROUP_TABLE,
|
||||
indices = [Index(SORT_ORDER)]
|
||||
)
|
||||
data class FeedGroupEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
|
|
@ -11,22 +11,24 @@ import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity.Compan
|
|||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
|
||||
@Entity(
|
||||
tableName = FEED_GROUP_SUBSCRIPTION_TABLE,
|
||||
primaryKeys = [GROUP_ID, SUBSCRIPTION_ID],
|
||||
indices = [Index(SUBSCRIPTION_ID)],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = FeedGroupEntity::class,
|
||||
parentColumns = [FeedGroupEntity.ID],
|
||||
childColumns = [GROUP_ID],
|
||||
onDelete = CASCADE, onUpdate = CASCADE, deferred = true),
|
||||
tableName = FEED_GROUP_SUBSCRIPTION_TABLE,
|
||||
primaryKeys = [GROUP_ID, SUBSCRIPTION_ID],
|
||||
indices = [Index(SUBSCRIPTION_ID)],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = FeedGroupEntity::class,
|
||||
parentColumns = [FeedGroupEntity.ID],
|
||||
childColumns = [GROUP_ID],
|
||||
onDelete = CASCADE, onUpdate = CASCADE, deferred = true
|
||||
),
|
||||
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = CASCADE, onUpdate = CASCADE, deferred = true)
|
||||
]
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = CASCADE, onUpdate = CASCADE, deferred = true
|
||||
)
|
||||
]
|
||||
)
|
||||
data class FeedGroupSubscriptionEntity(
|
||||
@ColumnInfo(name = GROUP_ID)
|
||||
|
|
|
@ -4,20 +4,21 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import java.time.OffsetDateTime
|
||||
import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.FEED_LAST_UPDATED_TABLE
|
||||
import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.SUBSCRIPTION_ID
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@Entity(
|
||||
tableName = FEED_LAST_UPDATED_TABLE,
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true)
|
||||
]
|
||||
tableName = FEED_LAST_UPDATED_TABLE,
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = SubscriptionEntity::class,
|
||||
parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
|
||||
childColumns = [SUBSCRIPTION_ID],
|
||||
onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
|
||||
)
|
||||
]
|
||||
)
|
||||
data class FeedLastUpdatedEntity(
|
||||
@PrimaryKey
|
||||
|
|
|
@ -8,7 +8,7 @@ import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE;
|
||||
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID;
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
|
||||
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
|
||||
|
|
|
@ -2,8 +2,8 @@ package org.schabi.newpipe.database.history.model
|
|||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Embedded
|
||||
import java.time.OffsetDateTime
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
data class StreamHistoryEntry(
|
||||
@Embedded
|
||||
|
@ -25,6 +25,6 @@ data class StreamHistoryEntry(
|
|||
|
||||
fun hasEqualValues(other: StreamHistoryEntry): Boolean {
|
||||
return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId &&
|
||||
accessDate.isEqual(other.accessDate)
|
||||
accessDate.isEqual(other.accessDate)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID;
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
package org.schabi.newpipe.database.stream.dao
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.reactivex.Flowable
|
||||
import java.time.OffsetDateTime
|
||||
import androidx.room.*
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import org.schabi.newpipe.database.BasicDAO
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID
|
||||
import org.schabi.newpipe.extractor.stream.StreamType
|
||||
import org.schabi.newpipe.extractor.stream.StreamType.AUDIO_LIVE_STREAM
|
||||
import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@Dao
|
||||
abstract class StreamDAO : BasicDAO<StreamEntity> {
|
||||
|
@ -35,10 +30,12 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
|||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
internal abstract fun silentInsertAllInternal(streams: List<StreamEntity>): List<Long>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT uid, stream_type, textual_upload_date, upload_date, is_upload_date_approximation, duration
|
||||
FROM streams WHERE url = :url AND service_id = :serviceId
|
||||
""")
|
||||
"""
|
||||
)
|
||||
internal abstract fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed?
|
||||
|
||||
@Transaction
|
||||
|
@ -79,7 +76,7 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
|||
|
||||
private fun compareAndUpdateStream(newerStream: StreamEntity) {
|
||||
val existentMinimalStream = getMinimalStreamForCompare(newerStream.serviceId, newerStream.url)
|
||||
?: throw IllegalStateException("Stream cannot be null just after insertion.")
|
||||
?: throw IllegalStateException("Stream cannot be null just after insertion.")
|
||||
newerStream.uid = existentMinimalStream.uid
|
||||
|
||||
val isNewerStreamLive = newerStream.streamType == AUDIO_LIVE_STREAM || newerStream.streamType == LIVE_STREAM
|
||||
|
@ -88,7 +85,7 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
|||
// Use the existent upload date if the newer stream does not have a better precision
|
||||
// (i.e. is an approximation). This is done to prevent unnecessary changes.
|
||||
val hasBetterPrecision =
|
||||
newerStream.uploadDate != null && newerStream.isUploadDateApproximation != true
|
||||
newerStream.uploadDate != null && newerStream.isUploadDateApproximation != true
|
||||
if (existentMinimalStream.uploadDate != null && !hasBetterPrecision) {
|
||||
newerStream.uploadDate = existentMinimalStream.uploadDate
|
||||
newerStream.textualUploadDate = existentMinimalStream.textualUploadDate
|
||||
|
@ -101,7 +98,8 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
|||
}
|
||||
}
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM streams WHERE
|
||||
|
||||
NOT EXISTS (SELECT 1 FROM stream_history sh
|
||||
|
@ -112,7 +110,8 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
|||
|
||||
AND NOT EXISTS (SELECT 1 FROM feed f
|
||||
WHERE f.stream_id = streams.uid)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun deleteOrphans(): Int
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
package org.schabi.newpipe.database.stream.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.OffsetDateTime
|
||||
import androidx.room.*
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_SERVICE_ID
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_TABLE
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_URL
|
||||
|
@ -15,11 +9,14 @@ 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.player.playqueue.PlayQueueItem
|
||||
import java.io.Serializable
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@Entity(tableName = STREAM_TABLE,
|
||||
indices = [
|
||||
Index(value = [STREAM_SERVICE_ID, STREAM_URL], unique = true)
|
||||
]
|
||||
@Entity(
|
||||
tableName = STREAM_TABLE,
|
||||
indices = [
|
||||
Index(value = [STREAM_SERVICE_ID, STREAM_URL], unique = true)
|
||||
]
|
||||
)
|
||||
data class StreamEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
@ -61,27 +58,27 @@ data class StreamEntity(
|
|||
) : Serializable {
|
||||
@Ignore
|
||||
constructor(item: StreamInfoItem) : this(
|
||||
serviceId = item.serviceId, url = item.url, title = item.name,
|
||||
streamType = item.streamType, duration = item.duration, uploader = item.uploaderName,
|
||||
thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount,
|
||||
textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(),
|
||||
isUploadDateApproximation = item.uploadDate?.isApproximation
|
||||
serviceId = item.serviceId, url = item.url, title = item.name,
|
||||
streamType = item.streamType, duration = item.duration, uploader = item.uploaderName,
|
||||
thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount,
|
||||
textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(),
|
||||
isUploadDateApproximation = item.uploadDate?.isApproximation
|
||||
)
|
||||
|
||||
@Ignore
|
||||
constructor(info: StreamInfo) : this(
|
||||
serviceId = info.serviceId, url = info.url, title = info.name,
|
||||
streamType = info.streamType, duration = info.duration, uploader = info.uploaderName,
|
||||
thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount,
|
||||
textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(),
|
||||
isUploadDateApproximation = info.uploadDate?.isApproximation
|
||||
serviceId = info.serviceId, url = info.url, title = info.name,
|
||||
streamType = info.streamType, duration = info.duration, uploader = info.uploaderName,
|
||||
thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount,
|
||||
textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(),
|
||||
isUploadDateApproximation = info.uploadDate?.isApproximation
|
||||
)
|
||||
|
||||
@Ignore
|
||||
constructor(item: PlayQueueItem) : this(
|
||||
serviceId = item.serviceId, url = item.url, title = item.title,
|
||||
streamType = item.streamType, duration = item.duration, uploader = item.uploader,
|
||||
thumbnailUrl = item.thumbnailUrl
|
||||
serviceId = item.serviceId, url = item.url, title = item.title,
|
||||
streamType = item.streamType, duration = item.duration, uploader = item.uploader,
|
||||
thumbnailUrl = item.thumbnailUrl
|
||||
)
|
||||
|
||||
fun toStreamInfoItem(): StreamInfoItem {
|
||||
|
|
|
@ -5,8 +5,8 @@ import androidx.room.Insert
|
|||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import org.schabi.newpipe.database.BasicDAO
|
||||
|
||||
@Dao
|
||||
|
@ -20,16 +20,19 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
|||
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
|
||||
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM subscriptions
|
||||
|
||||
WHERE name LIKE '%' || :filter || '%'
|
||||
|
||||
ORDER BY name COLLATE NOCASE ASC
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getSubscriptionsFiltered(filter: String): Flowable<List<SubscriptionEntity>>
|
||||
|
||||
@Query("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM subscriptions s
|
||||
|
||||
LEFT JOIN feed_group_subscription_join fgs
|
||||
|
@ -38,12 +41,14 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
|||
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("""
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM subscriptions s
|
||||
|
||||
LEFT JOIN feed_group_subscription_join fgs
|
||||
|
@ -53,7 +58,8 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
|||
AND s.name LIKE '%' || :filter || '%'
|
||||
|
||||
ORDER BY name COLLATE NOCASE ASC
|
||||
""")
|
||||
"""
|
||||
)
|
||||
abstract fun getSubscriptionsOnlyUngroupedFiltered(
|
||||
currentGroupId: Long,
|
||||
filter: String
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -34,6 +33,7 @@ import androidx.appcompat.view.menu.ActionMenuItemView;
|
|||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.nononsenseapps.filepicker.Utils;
|
||||
|
||||
|
@ -70,7 +70,7 @@ import java.util.Locale;
|
|||
|
||||
import icepick.Icepick;
|
||||
import icepick.State;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import us.shandian.giga.get.MissionRecoveryInfo;
|
||||
import us.shandian.giga.io.StoredDirectoryHelper;
|
||||
import us.shandian.giga.io.StoredFileHelper;
|
||||
|
|
|
@ -13,7 +13,7 @@ import android.widget.Toast;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
import com.jakewharton.rxbinding4.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
|
@ -34,8 +34,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
|
|
|
@ -15,9 +15,6 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.ViewTreeObserver;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Linkify;
|
||||
|
@ -27,6 +24,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -45,7 +43,9 @@ import androidx.appcompat.content.res.AppCompatResources;
|
|||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
|
@ -116,11 +116,11 @@ import java.util.concurrent.TimeUnit;
|
|||
import icepick.State;
|
||||
import io.noties.markwon.Markwon;
|
||||
import io.noties.markwon.linkify.LinkifyPlugin;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
|
||||
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
||||
|
|
|
@ -16,10 +16,10 @@ import org.schabi.newpipe.views.NewPipeRecyclerView;
|
|||
import java.util.Queue;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public abstract class BaseListInfoFragment<I extends ListInfo>
|
||||
extends BaseListFragment<I, ListExtractor.InfoItemsPage> {
|
||||
|
|
|
@ -24,7 +24,7 @@ import androidx.appcompat.app.ActionBar;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
import com.jakewharton.rxbinding4.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
||||
|
@ -53,15 +53,15 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Action;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.Action;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateBackgroundColor;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateTextColor;
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.schabi.newpipe.report.UserAction;
|
|||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
|
||||
public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.schabi.newpipe.util.KioskTranslator;
|
|||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
|
|
|
@ -51,12 +51,11 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.Disposables;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
|
@ -460,7 +459,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
.doFinally(() -> playlistEntity = null)
|
||||
.subscribe(ignored -> { /* Do nothing */ }, this::onError);
|
||||
} else {
|
||||
action = Disposables.empty();
|
||||
action = Disposable.empty();
|
||||
}
|
||||
|
||||
disposables.add(action);
|
||||
|
|
|
@ -68,13 +68,13 @@ import java.util.Queue;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
|
||||
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
|
||||
import static java.util.Arrays.asList;
|
||||
|
@ -709,7 +709,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
|
||||
final Observable<String> observable = suggestionPublisher
|
||||
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
|
||||
.startWith(searchString != null
|
||||
.startWithItem(searchString != null
|
||||
? searchString
|
||||
: "")
|
||||
.filter(ss -> isSuggestionsEnabled);
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.schabi.newpipe.fragments.list.videos;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -13,6 +12,7 @@ import android.widget.Switch;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
|
@ -25,8 +25,8 @@ import org.schabi.newpipe.util.RelatedStreamInfo;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
|
||||
public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInfo>
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
|
|
@ -33,11 +33,11 @@ import org.schabi.newpipe.util.OnClickGesture;
|
|||
import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
|
||||
@State
|
||||
|
|
|
@ -28,9 +28,9 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
||||
|
@ -98,7 +98,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final LocalPlaylistManager playlistManager =
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(requireContext()));
|
||||
|
||||
playlistAdapter = new LocalItemListAdapter(getActivity());
|
||||
playlistAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
|
||||
|
@ -113,7 +113,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
});
|
||||
|
||||
playlistRecyclerView = view.findViewById(R.id.playlist_list);
|
||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
playlistRecyclerView.setAdapter(playlistAdapter);
|
||||
|
||||
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||
|
@ -146,12 +146,12 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void openCreatePlaylistDialog() {
|
||||
if (getStreams() == null || getFragmentManager() == null) {
|
||||
if (getStreams() == null || !isAdded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlaylistCreationDialog.newInstance(getStreams()).show(getFragmentManager(), TAG);
|
||||
getDialog().dismiss();
|
||||
PlaylistCreationDialog.newInstance(getStreams()).show(getParentFragmentManager(), TAG);
|
||||
requireDialog().dismiss();
|
||||
}
|
||||
|
||||
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
|
||||
|
@ -183,6 +183,6 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> successToast.show()));
|
||||
|
||||
getDialog().dismiss();
|
||||
requireDialog().dismiss();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
|
||||
public final class PlaylistCreationDialog extends PlaylistDialog {
|
||||
public static PlaylistCreationDialog newInstance(final List<StreamEntity> streams) {
|
||||
|
|
|
@ -2,11 +2,11 @@ package org.schabi.newpipe.local.feed
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import java.time.LocalDate
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
@ -44,9 +44,9 @@ class FeedDatabaseManager(context: Context) {
|
|||
else -> feedTable.getAllStreamsFromGroup(groupId)
|
||||
}
|
||||
|
||||
return streams.map<List<StreamInfoItem>> {
|
||||
return streams.map {
|
||||
val items = ArrayList<StreamInfoItem>(it.size)
|
||||
it.mapTo(items) { it.toStreamInfoItem() }
|
||||
it.mapTo(items) { stream -> stream.toStreamInfoItem() }
|
||||
return@map items
|
||||
}
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ class FeedDatabaseManager(context: Context) {
|
|||
}
|
||||
|
||||
fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: OffsetDateTime) =
|
||||
feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold)
|
||||
feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold)
|
||||
|
||||
fun markAsOutdated(subscriptionId: Long) = feedTable
|
||||
.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
|
||||
.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
|
||||
|
||||
fun upsertAll(
|
||||
subscriptionId: Long,
|
||||
|
@ -113,38 +113,38 @@ class FeedDatabaseManager(context: Context) {
|
|||
|
||||
fun subscriptionIdsForGroup(groupId: Long): Flowable<List<Long>> {
|
||||
return feedGroupTable.getSubscriptionIdsFor(groupId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun updateSubscriptionsForGroup(groupId: Long, subscriptionIds: List<Long>): Completable {
|
||||
return Completable.fromCallable { feedGroupTable.updateSubscriptionsForGroup(groupId, subscriptionIds) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun createGroup(name: String, icon: FeedGroupIcon): Maybe<Long> {
|
||||
return Maybe.fromCallable { feedGroupTable.insert(FeedGroupEntity(0, name, icon)) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun getGroup(groupId: Long): Maybe<FeedGroupEntity> {
|
||||
return feedGroupTable.getGroup(groupId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun updateGroup(feedGroupEntity: FeedGroupEntity): Completable {
|
||||
return Completable.fromCallable { feedGroupTable.update(feedGroupEntity) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun deleteGroup(groupId: Long): Completable {
|
||||
return Completable.fromCallable { feedGroupTable.delete(groupId) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun updateGroupsOrder(groupIdList: List<Long>): Completable {
|
||||
|
@ -152,8 +152,8 @@ class FeedDatabaseManager(context: Context) {
|
|||
val orderMap = groupIdList.associateBy({ it }, { index++ })
|
||||
|
||||
return Completable.fromCallable { feedGroupTable.updateOrder(orderMap) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun oldestSubscriptionUpdate(groupId: Long): Flowable<List<OffsetDateTime>> {
|
||||
|
|
|
@ -37,7 +37,6 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.preference.PreferenceManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import icepick.State
|
||||
import java.util.Calendar
|
||||
import kotlinx.android.synthetic.main.error_retry.error_button_retry
|
||||
import kotlinx.android.synthetic.main.error_retry.error_message_view
|
||||
import kotlinx.android.synthetic.main.fragment_feed.empty_state_view
|
||||
|
@ -55,6 +54,7 @@ import org.schabi.newpipe.local.feed.service.FeedLoadService
|
|||
import org.schabi.newpipe.report.UserAction
|
||||
import org.schabi.newpipe.util.AnimationUtils.animateView
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import java.util.Calendar
|
||||
|
||||
class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||
private lateinit var viewModel: FeedViewModel
|
||||
|
@ -76,7 +76,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
groupId = arguments?.getLong(KEY_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID)
|
||||
?: FeedGroupEntity.GROUP_ALL_ID
|
||||
?: FeedGroupEntity.GROUP_ALL_ID
|
||||
groupName = arguments?.getString(KEY_GROUP_NAME) ?: ""
|
||||
}
|
||||
|
||||
|
@ -144,15 +144,15 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
}
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setMessage(R.string.feed_use_dedicated_fetch_method_help_text)
|
||||
.setNeutralButton(enableDisableButtonText) { _, _ ->
|
||||
sharedPreferences.edit {
|
||||
putBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), !usingDedicatedMethod)
|
||||
}
|
||||
.setMessage(R.string.feed_use_dedicated_fetch_method_help_text)
|
||||
.setNeutralButton(enableDisableButtonText) { _, _ ->
|
||||
sharedPreferences.edit {
|
||||
putBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), !usingDedicatedMethod)
|
||||
}
|
||||
.setPositiveButton(resources.getString(R.string.finish), null)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
.setPositiveButton(resources.getString(R.string.finish), null)
|
||||
.create()
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
showLoading()
|
||||
|
||||
val isIndeterminate = progressState.currentProgress == -1 &&
|
||||
progressState.maxProgress == -1
|
||||
progressState.maxProgress == -1
|
||||
|
||||
if (!isIndeterminate) {
|
||||
loading_progress_text.text = "${progressState.currentProgress}/${progressState.maxProgress}"
|
||||
|
@ -245,7 +245,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
}
|
||||
|
||||
loading_progress_bar.isIndeterminate = isIndeterminate ||
|
||||
(progressState.maxProgress > 0 && progressState.currentProgress == 0)
|
||||
(progressState.maxProgress > 0 && progressState.currentProgress == 0)
|
||||
loading_progress_bar.progress = progressState.currentProgress
|
||||
|
||||
loading_progress_bar.max = progressState.maxProgress
|
||||
|
@ -267,8 +267,10 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
}
|
||||
|
||||
if (loadedState.itemsErrors.isNotEmpty()) {
|
||||
showSnackBarError(loadedState.itemsErrors, UserAction.REQUESTED_FEED,
|
||||
"none", "Loading feed", R.string.general_error)
|
||||
showSnackBarError(
|
||||
loadedState.itemsErrors, UserAction.REQUESTED_FEED,
|
||||
"none", "Loading feed", R.string.general_error
|
||||
)
|
||||
}
|
||||
|
||||
if (loadedState.items.isEmpty()) {
|
||||
|
@ -311,9 +313,11 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
|||
override fun hasMoreItems() = false
|
||||
|
||||
private fun triggerUpdate() {
|
||||
getActivity()?.startService(Intent(requireContext(), FeedLoadService::class.java).apply {
|
||||
putExtra(FeedLoadService.EXTRA_GROUP_ID, groupId)
|
||||
})
|
||||
getActivity()?.startService(
|
||||
Intent(requireContext(), FeedLoadService::class.java).apply {
|
||||
putExtra(FeedLoadService.EXTRA_GROUP_ID, groupId)
|
||||
}
|
||||
)
|
||||
listState = null
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.schabi.newpipe.local.feed
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import java.util.Calendar
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import java.util.Calendar
|
||||
|
||||
sealed class FeedState {
|
||||
data class ProgressState(
|
||||
|
|
|
@ -5,21 +5,18 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.functions.Function4
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.functions.Function4
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.ktx.toCalendar
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ProgressEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.SuccessResultEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.*
|
||||
import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModel() {
|
||||
class Factory(val context: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModelProvider.Factory {
|
||||
|
@ -35,34 +32,34 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn
|
|||
val stateLiveData: LiveData<FeedState> = mutableStateLiveData
|
||||
|
||||
private var combineDisposable = Flowable
|
||||
.combineLatest(
|
||||
FeedEventManager.events(),
|
||||
feedDatabaseManager.asStreamItems(groupId),
|
||||
feedDatabaseManager.notLoadedCount(groupId),
|
||||
feedDatabaseManager.oldestSubscriptionUpdate(groupId),
|
||||
.combineLatest(
|
||||
FeedEventManager.events(),
|
||||
feedDatabaseManager.asStreamItems(groupId),
|
||||
feedDatabaseManager.notLoadedCount(groupId),
|
||||
feedDatabaseManager.oldestSubscriptionUpdate(groupId),
|
||||
Function4 { t1: FeedEventManager.Event, t2: List<StreamInfoItem>, t3: Long, t4: List<OffsetDateTime> ->
|
||||
return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull())
|
||||
}
|
||||
)
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { (event, listFromDB, notLoadedCount, oldestUpdate) ->
|
||||
val oldestUpdateCalendar = oldestUpdate?.toCalendar()
|
||||
|
||||
Function4 { t1: FeedEventManager.Event, t2: List<StreamInfoItem>, t3: Long, t4: List<OffsetDateTime> ->
|
||||
return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull())
|
||||
}
|
||||
)
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { (event, listFromDB, notLoadedCount, oldestUpdate) ->
|
||||
|
||||
val oldestUpdateCalendar = oldestUpdate?.toCalendar()
|
||||
|
||||
mutableStateLiveData.postValue(when (event) {
|
||||
mutableStateLiveData.postValue(
|
||||
when (event) {
|
||||
is IdleEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount)
|
||||
is ProgressEvent -> FeedState.ProgressState(event.currentProgress, event.maxProgress, event.progressMessage)
|
||||
is SuccessResultEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount, event.itemsErrors)
|
||||
is ErrorResultEvent -> FeedState.ErrorState(event.error)
|
||||
})
|
||||
|
||||
if (event is ErrorResultEvent || event is SuccessResultEvent) {
|
||||
FeedEventManager.reset()
|
||||
}
|
||||
)
|
||||
|
||||
if (event is ErrorResultEvent || event is SuccessResultEvent) {
|
||||
FeedEventManager.reset()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package org.schabi.newpipe.local.feed.service
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.processors.BehaviorProcessor
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.processors.BehaviorProcessor
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
object FeedEventManager {
|
||||
private var processor: BehaviorProcessor<Event> = BehaviorProcessor.create()
|
||||
private var ignoreUpstream = AtomicBoolean()
|
||||
private var eventsFlowable = processor.startWith(IdleEvent)
|
||||
private var eventsFlowable = processor.startWithItem(IdleEvent)
|
||||
|
||||
fun postEvent(event: Event) {
|
||||
processor.onNext(event)
|
||||
|
|
|
@ -31,21 +31,15 @@ import android.util.Log
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Notification
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.functions.Function
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.io.IOException
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Notification
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.functions.Consumer
|
||||
import io.reactivex.rxjava3.functions.Function
|
||||
import io.reactivex.rxjava3.processors.PublishProcessor
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import org.schabi.newpipe.MainActivity.DEBUG
|
||||
|
@ -55,14 +49,17 @@ import org.schabi.newpipe.extractor.ListInfo
|
|||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ProgressEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.SuccessResultEvent
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.*
|
||||
import org.schabi.newpipe.local.feed.service.FeedEventManager.postEvent
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.util.ExceptionUtils
|
||||
import org.schabi.newpipe.util.ExtractorHelper
|
||||
import java.io.IOException
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class FeedLoadService : Service() {
|
||||
companion object {
|
||||
|
@ -109,8 +106,11 @@ class FeedLoadService : Service() {
|
|||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "]," +
|
||||
" flags = [" + flags + "], startId = [" + startId + "]")
|
||||
Log.d(
|
||||
TAG,
|
||||
"onStartCommand() called with: intent = [" + intent + "]," +
|
||||
" flags = [" + flags + "], startId = [" + startId + "]"
|
||||
)
|
||||
}
|
||||
|
||||
if (intent == null || loadingSubscription != null) {
|
||||
|
@ -123,10 +123,10 @@ class FeedLoadService : Service() {
|
|||
|
||||
val groupId = intent.getLongExtra(EXTRA_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID)
|
||||
val useFeedExtractor = defaultSharedPreferences
|
||||
.getBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), false)
|
||||
.getBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), false)
|
||||
|
||||
val thresholdOutdatedSecondsString = defaultSharedPreferences
|
||||
.getString(getString(R.string.feed_update_threshold_key), getString(R.string.feed_update_threshold_default_value))
|
||||
.getString(getString(R.string.feed_update_threshold_key), getString(R.string.feed_update_threshold_default_value))
|
||||
val thresholdOutdatedSeconds = thresholdOutdatedSecondsString!!.toInt()
|
||||
|
||||
startLoading(groupId, useFeedExtractor, thresholdOutdatedSeconds)
|
||||
|
@ -181,63 +181,63 @@ class FeedLoadService : Service() {
|
|||
}
|
||||
|
||||
subscriptions
|
||||
.limit(1)
|
||||
.take(1)
|
||||
|
||||
.doOnNext {
|
||||
currentProgress.set(0)
|
||||
maxProgress.set(it.size)
|
||||
.doOnNext {
|
||||
currentProgress.set(0)
|
||||
maxProgress.set(it.size)
|
||||
}
|
||||
.filter { it.isNotEmpty() }
|
||||
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
startForeground(NOTIFICATION_ID, notificationBuilder.build())
|
||||
updateNotificationProgress(null)
|
||||
broadcastProgress()
|
||||
}
|
||||
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMap { Flowable.fromIterable(it) }
|
||||
.takeWhile { !cancelSignal.get() }
|
||||
|
||||
.parallel(PARALLEL_EXTRACTIONS, PARALLEL_EXTRACTIONS * 2)
|
||||
.runOn(Schedulers.io(), PARALLEL_EXTRACTIONS * 2)
|
||||
.filter { !cancelSignal.get() }
|
||||
|
||||
.map { subscriptionEntity ->
|
||||
try {
|
||||
val listInfo = if (useFeedExtractor) {
|
||||
ExtractorHelper
|
||||
.getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
|
||||
.blockingGet()
|
||||
} else {
|
||||
ExtractorHelper
|
||||
.getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
|
||||
.blockingGet()
|
||||
} as ListInfo<StreamInfoItem>
|
||||
|
||||
return@map Notification.createOnNext(Pair(subscriptionEntity.uid, listInfo))
|
||||
} catch (e: Throwable) {
|
||||
val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
|
||||
val wrapper = RequestException(subscriptionEntity.uid, request, e)
|
||||
return@map Notification.createOnError<Pair<Long, ListInfo<StreamInfoItem>>>(wrapper)
|
||||
}
|
||||
.filter { it.isNotEmpty() }
|
||||
}
|
||||
.sequential()
|
||||
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
startForeground(NOTIFICATION_ID, notificationBuilder.build())
|
||||
updateNotificationProgress(null)
|
||||
broadcastProgress()
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(errorHandlingConsumer)
|
||||
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMap { Flowable.fromIterable(it) }
|
||||
.takeWhile { !cancelSignal.get() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(notificationsConsumer)
|
||||
|
||||
.parallel(PARALLEL_EXTRACTIONS, PARALLEL_EXTRACTIONS * 2)
|
||||
.runOn(Schedulers.io(), PARALLEL_EXTRACTIONS * 2)
|
||||
.filter { !cancelSignal.get() }
|
||||
.observeOn(Schedulers.io())
|
||||
.buffer(BUFFER_COUNT_BEFORE_INSERT)
|
||||
.doOnNext(databaseConsumer)
|
||||
|
||||
.map { subscriptionEntity ->
|
||||
try {
|
||||
val listInfo = if (useFeedExtractor) {
|
||||
ExtractorHelper
|
||||
.getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
|
||||
.blockingGet()
|
||||
} else {
|
||||
ExtractorHelper
|
||||
.getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
|
||||
.blockingGet()
|
||||
} as ListInfo<StreamInfoItem>
|
||||
|
||||
return@map Notification.createOnNext(Pair(subscriptionEntity.uid, listInfo))
|
||||
} catch (e: Throwable) {
|
||||
val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
|
||||
val wrapper = RequestException(subscriptionEntity.uid, request, e)
|
||||
return@map Notification.createOnError<Pair<Long, ListInfo<StreamInfoItem>>>(wrapper)
|
||||
}
|
||||
}
|
||||
.sequential()
|
||||
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(errorHandlingConsumer)
|
||||
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(notificationsConsumer)
|
||||
|
||||
.observeOn(Schedulers.io())
|
||||
.buffer(BUFFER_COUNT_BEFORE_INSERT)
|
||||
.doOnNext(databaseConsumer)
|
||||
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(resultSubscriber)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(resultSubscriber)
|
||||
}
|
||||
|
||||
private fun broadcastProgress() {
|
||||
|
@ -274,7 +274,8 @@ class FeedLoadService : Service() {
|
|||
notificationUpdater.onNext(getString(R.string.feed_processing_message))
|
||||
postEvent(ProgressEvent(R.string.feed_processing_message))
|
||||
|
||||
disposables.add(Single
|
||||
disposables.add(
|
||||
Single
|
||||
.fromCallable {
|
||||
feedResultsHolder.ready()
|
||||
|
||||
|
@ -293,7 +294,8 @@ class FeedLoadService : Service() {
|
|||
return@subscribe
|
||||
}
|
||||
stopService()
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,16 +366,18 @@ class FeedLoadService : Service() {
|
|||
private var maxProgress = AtomicInteger(-1)
|
||||
|
||||
private fun createNotification(): NotificationCompat.Builder {
|
||||
val cancelActionIntent = PendingIntent.getBroadcast(this,
|
||||
NOTIFICATION_ID, Intent(ACTION_CANCEL), 0)
|
||||
val cancelActionIntent = PendingIntent.getBroadcast(
|
||||
this,
|
||||
NOTIFICATION_ID, Intent(ACTION_CANCEL), 0
|
||||
)
|
||||
|
||||
return NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setProgress(-1, -1, true)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.addAction(0, getString(R.string.cancel), cancelActionIntent)
|
||||
.setContentTitle(getString(R.string.feed_notification_loading))
|
||||
.setOngoing(true)
|
||||
.setProgress(-1, -1, true)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.addAction(0, getString(R.string.cancel), cancelActionIntent)
|
||||
.setContentTitle(getString(R.string.feed_notification_loading))
|
||||
}
|
||||
|
||||
private fun setupNotification() {
|
||||
|
@ -381,13 +385,15 @@ class FeedLoadService : Service() {
|
|||
notificationBuilder = createNotification()
|
||||
|
||||
val throttleAfterFirstEmission = Function { flow: Flowable<String> ->
|
||||
flow.limit(1).concatWith(flow.skip(1).throttleLatest(NOTIFICATION_SAMPLING_PERIOD.toLong(), TimeUnit.MILLISECONDS))
|
||||
flow.take(1).concatWith(flow.skip(1).throttleLatest(NOTIFICATION_SAMPLING_PERIOD.toLong(), TimeUnit.MILLISECONDS))
|
||||
}
|
||||
|
||||
disposables.add(notificationUpdater
|
||||
disposables.add(
|
||||
notificationUpdater
|
||||
.publish(throttleAfterFirstEmission)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateNotificationProgress))
|
||||
.subscribe(this::updateNotificationProgress)
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateNotificationProgress(updateDescription: String?) {
|
||||
|
|
|
@ -50,11 +50,11 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class HistoryRecordManager {
|
||||
private final AppDatabase database;
|
||||
|
|
|
@ -49,9 +49,9 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class StatisticsPlaylistFragment
|
||||
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
|
||||
|
|
|
@ -55,13 +55,12 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.Disposables;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
|
@ -641,7 +640,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
|
||||
private Disposable getDebouncedSaver() {
|
||||
if (debouncedSaveSignal == null) {
|
||||
return Disposables.empty();
|
||||
return Disposable.empty();
|
||||
}
|
||||
|
||||
return debouncedSaveSignal
|
||||
|
|
|
@ -15,11 +15,11 @@ import org.schabi.newpipe.database.stream.model.StreamEntity;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class LocalPlaylistManager {
|
||||
private final AppDatabase database;
|
||||
|
|
|
@ -7,9 +7,9 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class RemotePlaylistManager {
|
||||
|
||||
|
|
|
@ -28,13 +28,7 @@ import com.xwray.groupie.Item
|
|||
import com.xwray.groupie.Section
|
||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||
import icepick.State
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import kotlinx.android.synthetic.main.dialog_title.view.itemAdditionalDetails
|
||||
import kotlinx.android.synthetic.main.dialog_title.view.itemTitleView
|
||||
import kotlinx.android.synthetic.main.fragment_subscription.items_list
|
||||
|
@ -68,6 +62,12 @@ import org.schabi.newpipe.util.NavigationHelper
|
|||
import org.schabi.newpipe.util.OnClickGesture
|
||||
import org.schabi.newpipe.util.ShareUtils
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
|
||||
class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
||||
private lateinit var viewModel: SubscriptionViewModel
|
||||
|
@ -208,14 +208,19 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
if (!exportFile.parentFile.canWrite() || !exportFile.parentFile.canRead()) {
|
||||
Toast.makeText(activity, R.string.invalid_directory, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
activity.startService(Intent(activity, SubscriptionsExportService::class.java)
|
||||
.putExtra(KEY_FILE_PATH, exportFile.absolutePath))
|
||||
activity.startService(
|
||||
Intent(activity, SubscriptionsExportService::class.java)
|
||||
.putExtra(KEY_FILE_PATH, exportFile.absolutePath)
|
||||
)
|
||||
}
|
||||
} else if (requestCode == REQUEST_IMPORT_CODE) {
|
||||
val path = Utils.getFileForUri(data.data!!).absolutePath
|
||||
ImportConfirmationDialog.show(this, Intent(activity, SubscriptionsImportService::class.java)
|
||||
ImportConfirmationDialog.show(
|
||||
this,
|
||||
Intent(activity, SubscriptionsImportService::class.java)
|
||||
.putExtra(KEY_MODE, PREVIOUS_EXPORT_MODE)
|
||||
.putExtra(KEY_VALUE, path))
|
||||
.putExtra(KEY_VALUE, path)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,9 +252,9 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
|
||||
feedGroupsCarousel = FeedGroupCarouselItem(requireContext(), carouselAdapter)
|
||||
feedGroupsSortMenuItem = HeaderWithMenuItem(
|
||||
getString(R.string.feed_groups_header_title),
|
||||
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_sort),
|
||||
menuItemOnClickListener = ::openReorderDialog
|
||||
getString(R.string.feed_groups_header_title),
|
||||
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_sort),
|
||||
menuItemOnClickListener = ::openReorderDialog
|
||||
)
|
||||
add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel)))
|
||||
|
||||
|
@ -260,10 +265,11 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
subscriptionsSection.setHideWhenEmpty(true)
|
||||
|
||||
importExportItem = FeedImportExportItem(
|
||||
{ onImportPreviousSelected() },
|
||||
{ onImportFromServiceSelected(it) },
|
||||
{ onExportSelected() },
|
||||
importExportItemExpandedState ?: false)
|
||||
{ onImportPreviousSelected() },
|
||||
{ onImportFromServiceSelected(it) },
|
||||
{ onExportSelected() },
|
||||
importExportItemExpandedState ?: false
|
||||
)
|
||||
groupAdapter.add(Section(importExportItem, listOf(subscriptionsSection)))
|
||||
}
|
||||
|
||||
|
@ -284,8 +290,8 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
|
||||
private fun showLongTapDialog(selectedItem: ChannelInfoItem) {
|
||||
val commands = arrayOf(
|
||||
getString(R.string.share),
|
||||
getString(R.string.unsubscribe)
|
||||
getString(R.string.share),
|
||||
getString(R.string.unsubscribe)
|
||||
)
|
||||
|
||||
val actions = DialogInterface.OnClickListener { _, i ->
|
||||
|
@ -301,16 +307,18 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
bannerView.itemAdditionalDetails.visibility = View.GONE
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setCustomTitle(bannerView)
|
||||
.setItems(commands, actions)
|
||||
.create()
|
||||
.show()
|
||||
.setCustomTitle(bannerView)
|
||||
.setItems(commands, actions)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun deleteChannel(selectedItem: ChannelInfoItem) {
|
||||
disposables.add(subscriptionManager.deleteSubscription(selectedItem.serviceId, selectedItem.url).subscribe {
|
||||
Toast.makeText(requireContext(), getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
disposables.add(
|
||||
subscriptionManager.deleteSubscription(selectedItem.serviceId, selectedItem.url).subscribe {
|
||||
Toast.makeText(requireContext(), getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun doInitialLoadLogic() = Unit
|
||||
|
@ -332,8 +340,10 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
}
|
||||
|
||||
private val listenerChannelItem = object : OnClickGesture<ChannelInfoItem>() {
|
||||
override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment(fm,
|
||||
selectedItem.serviceId, selectedItem.url, selectedItem.name)
|
||||
override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment(
|
||||
fm,
|
||||
selectedItem.serviceId, selectedItem.url, selectedItem.name
|
||||
)
|
||||
|
||||
override fun held(selectedItem: ChannelInfoItem) = showLongTapDialog(selectedItem)
|
||||
}
|
||||
|
@ -420,14 +430,16 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
|
|||
|
||||
private fun shouldUseGridLayout(): Boolean {
|
||||
val listMode = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value))
|
||||
.getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value))
|
||||
|
||||
return when (listMode) {
|
||||
getString(R.string.list_view_mode_auto_key) -> {
|
||||
val configuration = resources.configuration
|
||||
|
||||
(configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
|
||||
configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE))
|
||||
(
|
||||
configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
|
||||
configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE)
|
||||
)
|
||||
}
|
||||
getString(R.string.list_view_mode_grid_key) -> true
|
||||
else -> false
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package org.schabi.newpipe.local.subscription
|
||||
|
||||
import android.content.Context
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.NewPipeDatabase
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionDAO
|
||||
|
@ -32,7 +32,8 @@ class SubscriptionManager(context: Context) {
|
|||
filterQuery.isNotEmpty() -> {
|
||||
return if (showOnlyUngrouped) {
|
||||
subscriptionTable.getSubscriptionsOnlyUngroupedFiltered(
|
||||
currentGroupId, filterQuery)
|
||||
currentGroupId, filterQuery
|
||||
)
|
||||
} else {
|
||||
subscriptionTable.getSubscriptionsFiltered(filterQuery)
|
||||
}
|
||||
|
@ -44,7 +45,8 @@ class SubscriptionManager(context: Context) {
|
|||
|
||||
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
||||
val listEntities = subscriptionTable.upsertAll(
|
||||
infoList.map { SubscriptionEntity.from(it) })
|
||||
infoList.map { SubscriptionEntity.from(it) }
|
||||
)
|
||||
|
||||
database.runInTransaction {
|
||||
infoList.forEachIndexed { index, info ->
|
||||
|
|
|
@ -5,12 +5,12 @@ import androidx.lifecycle.AndroidViewModel
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.xwray.groupie.Group
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.TimeUnit
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
import org.schabi.newpipe.local.subscription.item.ChannelItem
|
||||
import org.schabi.newpipe.local.subscription.item.FeedGroupCardItem
|
||||
import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SubscriptionViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(application)
|
||||
|
@ -22,22 +22,22 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
|
|||
val feedGroupsLiveData: LiveData<List<Group>> = mutableFeedGroupsLiveData
|
||||
|
||||
private var feedGroupItemsDisposable = feedDatabaseManager.groups()
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.map { it.map(::FeedGroupCardItem) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ mutableFeedGroupsLiveData.postValue(it) },
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
|
||||
)
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.map { it.map(::FeedGroupCardItem) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ mutableFeedGroupsLiveData.postValue(it) },
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
|
||||
)
|
||||
|
||||
private var stateItemsDisposable = subscriptionManager.subscriptions()
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.LoadedState(it)) },
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
|
||||
)
|
||||
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.LoadedState(it)) },
|
||||
{ mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
|
||||
)
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
|
|
@ -24,8 +24,6 @@ import com.xwray.groupie.Section
|
|||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||
import icepick.Icepick
|
||||
import icepick.State
|
||||
import java.io.Serializable
|
||||
import kotlin.collections.contains
|
||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.*
|
||||
import kotlinx.android.synthetic.main.toolbar_search_layout.*
|
||||
import org.schabi.newpipe.R
|
||||
|
@ -43,6 +41,8 @@ import org.schabi.newpipe.local.subscription.item.PickerIconItem
|
|||
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
|
||||
import org.schabi.newpipe.util.DeviceUtils
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import java.io.Serializable
|
||||
import kotlin.collections.contains
|
||||
|
||||
class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||
private lateinit var viewModel: FeedGroupDialogViewModel
|
||||
|
@ -116,21 +116,30 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProvider(this,
|
||||
FeedGroupDialogViewModel.Factory(requireContext(),
|
||||
groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped)
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
FeedGroupDialogViewModel.Factory(
|
||||
requireContext(),
|
||||
groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped
|
||||
)
|
||||
).get(FeedGroupDialogViewModel::class.java)
|
||||
|
||||
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
|
||||
viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer {
|
||||
setupSubscriptionPicker(it.first, it.second)
|
||||
})
|
||||
viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
|
||||
when (it) {
|
||||
ProcessingEvent -> disableInput()
|
||||
SuccessEvent -> dismiss()
|
||||
viewModel.subscriptionsLiveData.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
setupSubscriptionPicker(it.first, it.second)
|
||||
}
|
||||
})
|
||||
)
|
||||
viewModel.dialogEventLiveData.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
when (it) {
|
||||
ProcessingEvent -> disableInput()
|
||||
SuccessEvent -> dismiss()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
subscriptionGroupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||
add(subscriptionMainSection)
|
||||
|
@ -141,8 +150,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
// Disable animations, too distracting.
|
||||
itemAnimator = null
|
||||
adapter = subscriptionGroupAdapter
|
||||
layoutManager = GridLayoutManager(requireContext(), subscriptionGroupAdapter.spanCount,
|
||||
RecyclerView.VERTICAL, false).apply {
|
||||
layoutManager = GridLayoutManager(
|
||||
requireContext(), subscriptionGroupAdapter.spanCount,
|
||||
RecyclerView.VERTICAL, false
|
||||
).apply {
|
||||
spanSizeLookup = subscriptionGroupAdapter.spanSizeLookup
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +357,8 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
val selectedCount = this.selectedSubscriptions.size
|
||||
val selectedCountText = resources.getQuantityString(
|
||||
R.plurals.feed_group_dialog_selection_count,
|
||||
selectedCount, selectedCount)
|
||||
selectedCount, selectedCount
|
||||
)
|
||||
selected_subscription_count_view.text = selectedCountText
|
||||
subscriptions_header_info.text = selectedCountText
|
||||
}
|
||||
|
@ -401,10 +413,12 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
separator.onlyVisibleIn(SubscriptionsPickerScreen, IconPickerScreen)
|
||||
cancel_button.onlyVisibleIn(InitialScreen, DeleteScreen)
|
||||
|
||||
confirm_button.setText(when {
|
||||
currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create
|
||||
else -> android.R.string.ok
|
||||
})
|
||||
confirm_button.setText(
|
||||
when {
|
||||
currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create
|
||||
else -> android.R.string.ok
|
||||
}
|
||||
)
|
||||
|
||||
delete_button.isGone = currentScreen != InitialScreen || groupId == NO_GROUP_SELECTED
|
||||
|
||||
|
@ -454,8 +468,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
}
|
||||
|
||||
private fun hideKeyboardSearch() {
|
||||
inputMethodManager.hideSoftInputFromWindow(toolbar_search_edit_text.windowToken,
|
||||
InputMethodManager.RESULT_UNCHANGED_SHOWN)
|
||||
inputMethodManager.hideSoftInputFromWindow(
|
||||
toolbar_search_edit_text.windowToken,
|
||||
InputMethodManager.RESULT_UNCHANGED_SHOWN
|
||||
)
|
||||
toolbar_search_edit_text.clearFocus()
|
||||
}
|
||||
|
||||
|
@ -466,8 +482,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
}
|
||||
|
||||
private fun hideKeyboard() {
|
||||
inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken,
|
||||
InputMethodManager.RESULT_UNCHANGED_SHOWN)
|
||||
inputMethodManager.hideSoftInputFromWindow(
|
||||
group_name_input.windowToken,
|
||||
InputMethodManager.RESULT_UNCHANGED_SHOWN
|
||||
)
|
||||
group_name_input.clearFocus()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.processors.BehaviorProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.functions.BiFunction
|
||||
import io.reactivex.rxjava3.processors.BehaviorProcessor
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
||||
|
@ -32,9 +32,9 @@ class FeedGroupDialogViewModel(
|
|||
|
||||
private var subscriptionsFlowable = Flowable
|
||||
.combineLatest(
|
||||
filterSubscriptions.startWith(initialQuery),
|
||||
toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
|
||||
BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
|
||||
filterSubscriptions.startWithItem(initialQuery),
|
||||
toggleShowOnlyUngrouped.startWithItem(initialShowOnlyUngrouped),
|
||||
BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.switchMap { (query, showOnlyUngrouped) ->
|
||||
|
@ -55,8 +55,10 @@ class FeedGroupDialogViewModel(
|
|||
.subscribe(mutableGroupLiveData::postValue)
|
||||
|
||||
private var subscriptionsDisposable = Flowable
|
||||
.combineLatest(subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
||||
BiFunction { t1: List<PickerSubscriptionItem>, t2: List<Long> -> t1 to t2.toSet() })
|
||||
.combineLatest(
|
||||
subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
||||
BiFunction { t1: List<PickerSubscriptionItem>, t2: List<Long> -> t1 to t2.toSet() }
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(mutableSubscriptionsLiveData::postValue)
|
||||
|
||||
|
@ -68,15 +70,19 @@ class FeedGroupDialogViewModel(
|
|||
}
|
||||
|
||||
fun createGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>) {
|
||||
doAction(feedDatabaseManager.createGroup(name, selectedIcon)
|
||||
.flatMapCompletable {
|
||||
feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
|
||||
})
|
||||
doAction(
|
||||
feedDatabaseManager.createGroup(name, selectedIcon)
|
||||
.flatMapCompletable {
|
||||
feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set<Long>, sortOrder: Long) {
|
||||
doAction(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
|
||||
.andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
|
||||
doAction(
|
||||
feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
|
||||
.andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder)))
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteGroup() {
|
||||
|
@ -120,8 +126,10 @@ class FeedGroupDialogViewModel(
|
|||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return FeedGroupDialogViewModel(context.applicationContext,
|
||||
groupId, initialQuery, initialShowOnlyUngrouped) as T
|
||||
return FeedGroupDialogViewModel(
|
||||
context.applicationContext,
|
||||
groupId, initialQuery, initialShowOnlyUngrouped
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.xwray.groupie.TouchCallback
|
|||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||
import icepick.Icepick
|
||||
import icepick.State
|
||||
import java.util.Collections
|
||||
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.confirm_button
|
||||
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.feed_groups_list
|
||||
import org.schabi.newpipe.R
|
||||
|
@ -25,6 +24,7 @@ import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewMo
|
|||
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent
|
||||
import org.schabi.newpipe.local.subscription.item.FeedGroupReorderItem
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import java.util.Collections
|
||||
|
||||
class FeedGroupReorderDialog : DialogFragment() {
|
||||
private lateinit var viewModel: FeedGroupReorderDialogViewModel
|
||||
|
@ -51,12 +51,15 @@ class FeedGroupReorderDialog : DialogFragment() {
|
|||
|
||||
viewModel = ViewModelProvider(this).get(FeedGroupReorderDialogViewModel::class.java)
|
||||
viewModel.groupsLiveData.observe(viewLifecycleOwner, Observer(::handleGroups))
|
||||
viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
|
||||
when (it) {
|
||||
ProcessingEvent -> disableInput()
|
||||
SuccessEvent -> dismiss()
|
||||
viewModel.dialogEventLiveData.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
when (it) {
|
||||
ProcessingEvent -> disableInput()
|
||||
SuccessEvent -> dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
feed_groups_list.layoutManager = LinearLayoutManager(requireContext())
|
||||
feed_groups_list.adapter = groupAdapter
|
||||
|
|
|
@ -4,9 +4,9 @@ import android.app.Application
|
|||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||
|
||||
|
@ -21,9 +21,9 @@ class FeedGroupReorderDialogViewModel(application: Application) : AndroidViewMod
|
|||
private var actionProcessingDisposable: Disposable? = null
|
||||
|
||||
private var groupsDisposable = feedDatabaseManager.groups()
|
||||
.limit(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(mutableGroupsLiveData::postValue)
|
||||
.take(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(mutableGroupsLiveData::postValue)
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
@ -40,8 +40,8 @@ class FeedGroupReorderDialogViewModel(application: Application) : AndroidViewMod
|
|||
mutableDialogEventLiveData.value = DialogEvent.ProcessingEvent
|
||||
|
||||
actionProcessingDisposable = completable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ class ChannelItem(
|
|||
viewHolder.itemAdditionalDetails.text = getDetailLine(viewHolder.root.context)
|
||||
if (itemVersion == ItemVersion.NORMAL) viewHolder.itemChannelDescriptionView.text = infoItem.description
|
||||
|
||||
ImageLoader.getInstance().displayImage(infoItem.thumbnailUrl, viewHolder.itemThumbnailView,
|
||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS)
|
||||
ImageLoader.getInstance().displayImage(
|
||||
infoItem.thumbnailUrl, viewHolder.itemThumbnailView,
|
||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS
|
||||
)
|
||||
|
||||
gesturesListener?.run {
|
||||
viewHolder.containerView.setOnClickListener { selected(infoItem) }
|
||||
|
|
|
@ -20,7 +20,7 @@ data class FeedGroupReorderItem(
|
|||
val dragCallback: ItemTouchHelper
|
||||
) : Item() {
|
||||
constructor (feedGroupEntity: FeedGroupEntity, dragCallback: ItemTouchHelper) :
|
||||
this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon, dragCallback)
|
||||
this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon, dragCallback)
|
||||
|
||||
override fun getId(): Long {
|
||||
return when (groupId) {
|
||||
|
|
|
@ -49,8 +49,10 @@ class FeedImportExportItem(
|
|||
|
||||
expandIconListener?.let { viewHolder.import_export_options.removeListener(it) }
|
||||
expandIconListener = CollapsibleView.StateListener { newState ->
|
||||
AnimationUtils.animateRotation(viewHolder.import_export_expand_icon,
|
||||
250, if (newState == CollapsibleView.COLLAPSED) 0 else 180)
|
||||
AnimationUtils.animateRotation(
|
||||
viewHolder.import_export_expand_icon,
|
||||
250, if (newState == CollapsibleView.COLLAPSED) 0 else 180
|
||||
)
|
||||
}
|
||||
|
||||
viewHolder.import_export_options.currentState = if (isExpanded) CollapsibleView.EXPANDED else CollapsibleView.COLLAPSED
|
||||
|
@ -85,8 +87,10 @@ class FeedImportExportItem(
|
|||
}
|
||||
|
||||
private fun setupImportFromItems(listHolder: ViewGroup) {
|
||||
val previousBackupItem = addItemView(listHolder.context.getString(R.string.previous_export),
|
||||
ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_backup), listHolder)
|
||||
val previousBackupItem = addItemView(
|
||||
listHolder.context.getString(R.string.previous_export),
|
||||
ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_backup), listHolder
|
||||
)
|
||||
previousBackupItem.setOnClickListener { onImportPreviousSelected() }
|
||||
|
||||
val iconColor = if (ThemeHelper.isLightThemeSelected(listHolder.context)) Color.BLACK else Color.WHITE
|
||||
|
@ -112,8 +116,10 @@ class FeedImportExportItem(
|
|||
}
|
||||
|
||||
private fun setupExportToItems(listHolder: ViewGroup) {
|
||||
val previousBackupItem = addItemView(listHolder.context.getString(R.string.file),
|
||||
ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_save), listHolder)
|
||||
val previousBackupItem = addItemView(
|
||||
listHolder.context.getString(R.string.file),
|
||||
ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_save), listHolder
|
||||
)
|
||||
previousBackupItem.setOnClickListener { onExportSelected() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ class HeaderWithMenuItem(
|
|||
viewHolder.header_menu_item.setImageResource(itemIcon)
|
||||
|
||||
val listener: OnClickListener? =
|
||||
onClickListener?.let { OnClickListener { onClickListener.invoke() } }
|
||||
onClickListener?.let { OnClickListener { onClickListener.invoke() } }
|
||||
viewHolder.root.setOnClickListener(listener)
|
||||
|
||||
val menuItemListener: OnClickListener? =
|
||||
menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } }
|
||||
menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } }
|
||||
viewHolder.header_menu_item.setOnClickListener(menuItemListener)
|
||||
updateMenuItemVisibility(viewHolder)
|
||||
}
|
||||
|
|
|
@ -22,8 +22,10 @@ data class PickerSubscriptionItem(
|
|||
override fun getSpanSize(spanCount: Int, position: Int): Int = 1
|
||||
|
||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||
ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl,
|
||||
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
|
||||
ImageLoader.getInstance().displayImage(
|
||||
subscriptionEntity.avatarUrl,
|
||||
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS
|
||||
)
|
||||
|
||||
viewHolder.title_view.text = subscriptionEntity.name
|
||||
viewHolder.selected_highlight.isVisible = isSelected
|
||||
|
@ -39,7 +41,9 @@ data class PickerSubscriptionItem(
|
|||
|
||||
fun updateSelected(containerView: View, isSelected: Boolean) {
|
||||
this.isSelected = isSelected
|
||||
animateView(containerView.selected_highlight,
|
||||
AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
|
||||
animateView(
|
||||
containerView.selected_highlight,
|
||||
AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,11 +46,11 @@ import java.util.Collections;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.processors.PublishProcessor;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.processors.PublishProcessor;
|
||||
|
||||
public abstract class BaseImportExportService extends Service {
|
||||
protected final String TAG = this.getClass().getSimpleName();
|
||||
|
@ -120,7 +120,7 @@ public abstract class BaseImportExportService extends Service {
|
|||
startForeground(getNotificationId(), notificationBuilder.build());
|
||||
|
||||
final Function<Flowable<String>, Publisher<String>> throttleAfterFirstEmission = flow ->
|
||||
flow.limit(1).concatWith(flow.skip(1)
|
||||
flow.take(1).concatWith(flow.skip(1)
|
||||
.throttleLast(NOTIFICATION_SAMPLING_PERIOD, TimeUnit.MILLISECONDS));
|
||||
|
||||
disposables.add(notificationUpdater
|
||||
|
|
|
@ -37,9 +37,9 @@ import java.io.FileOutputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ import java.io.InputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Notification;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Notification;
|
||||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.functions.Function;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
|
||||
|
|
|
@ -27,13 +27,13 @@ import android.content.SharedPreferences;
|
|||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.AudioManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
|
@ -77,17 +77,16 @@ import org.schabi.newpipe.util.SerializedCache;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.SerialDisposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.disposables.SerialDisposable;
|
||||
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
|
||||
import static io.reactivex.android.schedulers.AndroidSchedulers.mainThread;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
/**
|
||||
|
@ -720,8 +719,9 @@ public abstract class BasePlayer implements
|
|||
}
|
||||
|
||||
private Disposable getProgressReactor() {
|
||||
return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, MILLISECONDS, mainThread())
|
||||
.observeOn(mainThread())
|
||||
return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, MILLISECONDS,
|
||||
AndroidSchedulers.mainThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> triggerProgressUpdate(),
|
||||
error -> Log.e(TAG, "Progress update failure: ", error));
|
||||
}
|
||||
|
@ -1319,7 +1319,7 @@ public abstract class BasePlayer implements
|
|||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true)) {
|
||||
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
|
||||
.observeOn(mainThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnError((e) -> {
|
||||
if (DEBUG) {
|
||||
e.printStackTrace();
|
||||
|
@ -1339,7 +1339,7 @@ public abstract class BasePlayer implements
|
|||
if (prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true)) {
|
||||
final Disposable stateSaver = queueItem.getStream()
|
||||
.flatMapCompletable(info -> recordManager.saveStreamState(info, 0))
|
||||
.observeOn(mainThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnError((e) -> {
|
||||
if (DEBUG) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -30,14 +30,14 @@ import java.util.Set;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.internal.subscriptions.EmptySubscription;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException;
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException;
|
||||
|
|
|
@ -14,8 +14,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.core.SingleObserver;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
|
||||
boolean isInitial;
|
||||
|
|
|
@ -9,8 +9,8 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, ChannelInfoItem> {
|
||||
public ChannelPlayQueue(final ChannelInfoItem item) {
|
||||
|
|
|
@ -21,10 +21,10 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.reactivex.BackpressureStrategy;
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* PlayQueue is responsible for keeping track of a list of streams and the index of
|
||||
|
@ -80,7 +80,7 @@ public abstract class PlayQueue implements Serializable {
|
|||
|
||||
broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.startWith(new InitEvent());
|
||||
.startWithItem(new InitEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.schabi.newpipe.util.FallbackViewHolder;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 01.08.16.
|
||||
|
|
|
@ -10,8 +10,8 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class PlayQueueItem implements Serializable {
|
||||
public static final long RECOVERY_UNSET = Long.MIN_VALUE;
|
||||
|
|
|
@ -8,8 +8,8 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo, PlaylistInfoItem> {
|
||||
public PlaylistPlayQueue(final PlaylistInfoItem item) {
|
||||
|
|
|
@ -14,9 +14,9 @@ import org.schabi.newpipe.report.ErrorInfo;
|
|||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.InfoCache;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||
private String cacheWipeKey;
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -26,6 +25,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -44,11 +44,11 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class PeertubeInstanceListFragment extends Fragment {
|
||||
private static final int MENU_ITEM_RESTORE_ID = 123456;
|
||||
|
|
|
@ -30,10 +30,10 @@ import java.util.List;
|
|||
import java.util.Vector;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 26.09.17.
|
||||
|
|
|
@ -33,9 +33,9 @@ import org.schabi.newpipe.report.UserAction;
|
|||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class SelectPlaylistFragment extends DialogFragment {
|
||||
/**
|
||||
|
|
|
@ -23,9 +23,9 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class CommentTextOnTouchListener implements View.OnTouchListener {
|
||||
public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener();
|
||||
|
|
|
@ -10,9 +10,11 @@ class ExceptionUtils {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun isInterruptedCaused(throwable: Throwable): Boolean {
|
||||
return hasExactCause(throwable,
|
||||
InterruptedIOException::class.java,
|
||||
InterruptedException::class.java)
|
||||
return hasExactCause(
|
||||
throwable,
|
||||
InterruptedIOException::class.java,
|
||||
InterruptedException::class.java
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,8 +22,10 @@ class ExceptionUtils {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun isNetworkRelated(throwable: Throwable): Boolean {
|
||||
return hasAssignableCause(throwable,
|
||||
IOException::class.java)
|
||||
return hasAssignableCause(
|
||||
throwable,
|
||||
IOException::class.java
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,8 +57,8 @@ import org.schabi.newpipe.report.UserAction;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
|
||||
public final class ExtractorHelper {
|
||||
private static final String TAG = ExtractorHelper.class.getSimpleName();
|
||||
|
|
|
@ -23,9 +23,9 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import us.shandian.giga.util.Utility;
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,10 +49,10 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
import us.shandian.giga.get.FinishedMission;
|
||||
import us.shandian.giga.get.Mission;
|
||||
|
|
|
@ -13,8 +13,10 @@ class FeedGroupIconTest {
|
|||
val added = usedIds.add(currentIcon.id)
|
||||
assertTrue("Repeated ids (current item: ${currentIcon.name} - ${currentIcon.id})", added)
|
||||
|
||||
assertEquals("Gap between ids detected (current item: ${currentIcon.name} - ${currentIcon.id} → should be: $shouldBeId)",
|
||||
shouldBeId, currentIcon.id)
|
||||
assertEquals(
|
||||
"Gap between ids detected (current item: ${currentIcon.name} - ${currentIcon.id} → should be: $shouldBeId)",
|
||||
shouldBeId, currentIcon.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.schabi.newpipe.util
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InterruptedIOException
|
||||
import java.net.SocketException
|
||||
import javax.net.ssl.SSLException
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.schabi.newpipe.util.ExceptionUtils.Companion.hasAssignableCause
|
||||
import org.schabi.newpipe.util.ExceptionUtils.Companion.hasExactCause
|
||||
import java.io.IOException
|
||||
import java.io.InterruptedIOException
|
||||
import java.net.SocketException
|
||||
import javax.net.ssl.SSLException
|
||||
|
||||
class ExceptionUtilsTest {
|
||||
@Test fun `assignable causes`() {
|
||||
|
|
|
@ -9,7 +9,8 @@ import org.junit.Test
|
|||
class UrlFinderTest {
|
||||
@Test fun `first url from long text`() {
|
||||
val expected = "https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_"
|
||||
val result = UrlFinder.firstUrlFromInput("""
|
||||
val result = UrlFinder.firstUrlFromInput(
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
|Eu tincidunt tortor aliquam nulla. URL: https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ Sed dictum consequat dui.
|
||||
|Pharetra diam sit amet nisl suscipit adipiscing bibendum est.
|
||||
|
@ -18,13 +19,15 @@ class UrlFinderTest {
|
|||
|Dapibus ultrices in iaculis nunc sed augue lacus viverra. Nisl purus in mollis nunc.
|
||||
|Viverra nibh cras pulvinar mattis. ####!@!@!@!#### Not this one: https://www.youtube.com/playlist?list=SHOULD_NOT Nunc sed blandit libero volutpat.
|
||||
|Nisl tincidunt eget nullam non nisi est sit amet. Purus in massa tempor nec feugiat nisl pretium fusce id.
|
||||
|Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin())
|
||||
|Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin()
|
||||
)
|
||||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test fun `no url from long text`() {
|
||||
val result = UrlFinder.firstUrlFromInput("""
|
||||
val result = UrlFinder.firstUrlFromInput(
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
|Eu tincidunt tortor aliquam nulla. Sed dictum consequat dui. Pharetra diam sit amet nisl suscipit adipiscing bibendum est.
|
||||
|Volutpat sed cras ornare arcu dui vivamus. Nulla posuere sollicitudin aliquam ultrices sagittis.
|
||||
|
@ -32,7 +35,8 @@ class UrlFinderTest {
|
|||
|Dapibus ultrices in iaculis nunc sed augue lacus viverra. Nisl purus in mollis nunc.
|
||||
|Viverra nibh cras pulvinar mattis. Not this one: sed blandit libero volutpat.
|
||||
|Nisl tincidunt eget nullam non nisi est sit amet. Purus in massa tempor nec feugiat nisl pretium fusce id.
|
||||
|Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin())
|
||||
|Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin()
|
||||
)
|
||||
|
||||
assertEquals(null, result)
|
||||
}
|
||||
|
@ -44,14 +48,20 @@ class UrlFinderTest {
|
|||
}
|
||||
|
||||
@Test fun `normal urls`() {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_")
|
||||
)
|
||||
|
||||
assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||
)
|
||||
|
||||
assertEquals("http://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("http://www.youtube.com/watch?v=dQw4w9WgXcQ"))
|
||||
assertEquals(
|
||||
"http://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("http://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||
)
|
||||
|
||||
assertEquals("https://www.google.com", UrlFinder.firstUrlFromInput("https://www.google.com"))
|
||||
assertEquals("http://www.google.com/test/", UrlFinder.firstUrlFromInput("http://www.google.com/test/"))
|
||||
|
@ -79,21 +89,33 @@ class UrlFinderTest {
|
|||
}
|
||||
|
||||
@Test fun `random prefixes and suffixes`() {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("$#!@#@!#https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ @@@@@@@@@@@"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("$#!@#@!#https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ @@@@@@@@@@@")
|
||||
)
|
||||
|
||||
assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("(___\"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_\")))_"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
|
||||
UrlFinder.firstUrlFromInput("(___\"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_\")))_")
|
||||
)
|
||||
|
||||
assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput(" https://www.youtube.com/watch?v=dQw4w9WgXcQ "))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput(" https://www.youtube.com/watch?v=dQw4w9WgXcQ ")
|
||||
)
|
||||
|
||||
assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput(" ------_---__-https://www.youtube.com/watch?v=dQw4w9WgXcQ !!!!!!"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput(" ------_---__-https://www.youtube.com/watch?v=dQw4w9WgXcQ !!!!!!")
|
||||
)
|
||||
|
||||
assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("****https://www.youtube.com/watch?v=dQw4w9WgXcQ _"))
|
||||
assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ\"Not PartOfTheUrl"))
|
||||
assertEquals(
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("****https://www.youtube.com/watch?v=dQw4w9WgXcQ _")
|
||||
)
|
||||
assertEquals(
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ\"Not PartOfTheUrl")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.72'
|
||||
ext.kotlin_version = '1.4.10'
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,4 +1,4 @@
|
|||
#Sat Oct 17 06:10:46 IST 2020
|
||||
#Thu Oct 15 11:41:05 CEST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
Loading…
Reference in a new issue