dFCu#~^lZNAz0o9GUzbRi6|8%^Z(sV($+LXsw`BcW@9?)L)^>72Rbn7R6$9C|
zqo2*v>^1mlJ?tp}PfTgNj!|X9jS_!ayrV)n1MmRw0Pq0t0Pt)954i|h?gl(gqkzX$
z1w4-C=ga{qz41pO(6c9H=xM&EP}SuTFwa?~U2J9$&Af_cHX13~&a4sg6ofU`eg}#N
zh^MuP#{lBFi9{L#Jit4(;T`=Dv-tPmog=J78pS)7D&8Ts=9&Ye^fOWj?(BpNi39{%~U>{%~V4qUh$5IGwksle_
za+I-;FQ;3t0qnCcXJ`_6d%gCfpYzH)le431u4z`Z
zADPVTs@G*PHP2J6G+nSR%b}=Ye!%qe11jZA9%5bBMJ--yYipKe`_;Eqs<4BI7(fv~
z5mKI!@(ftytHi{P@`sE@agkRUE~D&4{KuI;p&^QoTO_
zApju&Apju&ApoJ{#qNd*5b~=ALKlnn`sxe6%sji+^fb?OYF+xZCnrybCPaR;OwBi2
zy!BZH+ACJ7*LA9>B3P(YudV5olq^K4-bchSS1a{KNtC^@FqlSJKF(w)&x`WBD9?-X
zyeQB6xaE0gRJ>ugC~QE|rNX>(9>b(z(p@s?(LzoRL709daY{pmV$-nhF71RSqAAM+
zoz7s_u50yP~YCq@U?Zle&6@%EE9JmY50_vL^HjCVM~9#tmKE-
z+^-5176}-5nnUag_I1PFe(enyG`6>2OfwP8XCW0A+!{m-5HUc+01*R33?3q4fNjsP
z?O9i8)|*(Q4#Sr27lYRhuf3if{S5UT{qjBKohG*Q+c@g^t~RDItU~Z(tadnm9p^lJ
z>3G@1y|bm|x!2pS^?KYC#lW!I0jnLb+5xK_u-ZXowZmQIzzkir9@y%0Oc#uQ`Ly~V
T|4lfZe);r&fdl#HS9t*dv-=o4
From e0c674bc9e93a20848f4b3fd796d44d80341299f Mon Sep 17 00:00:00 2001
From: Koitharu
Date: Mon, 24 May 2021 19:32:56 +0300
Subject: [PATCH 002/213] Move player notification settings into appearance
section
---
app/src/debug/res/xml/main_settings.xml | 6 -----
.../local/subscription/SubscriptionManager.kt | 24 +++++++++----------
.../settings/AppearanceSettingsFragment.java | 2 +-
app/src/main/res/values-ru/strings.xml | 1 +
app/src/main/res/values-uk/strings.xml | 1 +
app/src/main/res/values/strings.xml | 1 +
app/src/main/res/xml/appearance_settings.xml | 7 ++++++
7 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/app/src/debug/res/xml/main_settings.xml b/app/src/debug/res/xml/main_settings.xml
index 4e812bb1c..1b1c17e85 100644
--- a/app/src/debug/res/xml/main_settings.xml
+++ b/app/src/debug/res/xml/main_settings.xml
@@ -34,12 +34,6 @@
android:title="@string/content"
app:iconSpaceReserved="false" />
-
-
- Completable.fromAction {
- entity.notificationMode = mode
- subscriptionTable().update(entity)
- }.andThen(rememberLastStream(entity))
- }
+ .flatMapCompletable { entity: SubscriptionEntity ->
+ Completable.fromAction {
+ entity.notificationMode = mode
+ subscriptionTable().update(entity)
+ }.andThen(rememberLastStream(entity))
+ }
}
fun updateFromInfo(subscriptionId: Long, info: ListInfo) {
@@ -110,11 +110,11 @@ class SubscriptionManager(context: Context) {
private fun rememberLastStream(subscription: SubscriptionEntity): Completable {
return ExtractorHelper.getChannelInfo(subscription.serviceId, subscription.url, false)
- .map { channel -> channel.relatedItems.map { stream -> StreamEntity(stream) } }
- .flatMapCompletable { entities ->
- Completable.fromAction {
- database.streamDAO().upsertAll(entities)
- }
- }.onErrorComplete()
+ .map { channel -> channel.relatedItems.map { stream -> StreamEntity(stream) } }
+ .flatMapCompletable { entities ->
+ Completable.fromAction {
+ database.streamDAO().upsertAll(entities)
+ }
+ }.onErrorComplete()
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
index 1e1b03b4f..e363469c8 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java
@@ -60,7 +60,7 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
- if (preference.getKey().equals(captionSettingsKey) && CAPTIONING_SETTINGS_ACCESSIBLE) {
+ if (captionSettingsKey.equals(preference.getKey()) && CAPTIONING_SETTINGS_ACCESSIBLE) {
try {
startActivity(new Intent(Settings.ACTION_CAPTIONING_SETTINGS));
} catch (final ActivityNotFoundException e) {
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index f9280663b..30821be51 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -569,6 +569,7 @@
Подтверждать очистку очереди
Переход от одного плеера к другому может заменить вашу очередь
Уведомление
+ Настроить уведомление о воспроизводимом сейчас потоке
Ничего
Буферизация
Перемешать
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 0795d378c..7dc699202 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -582,6 +582,7 @@
Повʼязані елементи
Коментарі
Сповіщення
+ Налаштувати повідомлення про відтворюваний наразі потік
Не розпізнано URL. Відкрити через іншу програму\?
Самододавання в чергу
Показувати метадані
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f84cc83b1..40dcd17c9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -147,6 +147,7 @@
Debug
Updates
Notification
+ Configure current playing stream notification
Playing in background
Playing in popup mode
Content
diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml
index f0c6f2aa1..6bc9f3381 100644
--- a/app/src/main/res/xml/appearance_settings.xml
+++ b/app/src/main/res/xml/appearance_settings.xml
@@ -57,4 +57,11 @@
android:title="@string/tablet_mode_title"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
+
+
+
From c95aec9da68525707bbe3d5e96d4336ef2e91bd2 Mon Sep 17 00:00:00 2001
From: Koitharu
Date: Mon, 21 Jun 2021 16:51:23 +0300
Subject: [PATCH 003/213] Fix database test
---
.../newpipe/database/AppDatabaseTest.kt | 144 ++++++++++++++++++
1 file changed, 144 insertions(+)
create mode 100644 app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
new file mode 100644
index 000000000..b80120074
--- /dev/null
+++ b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
@@ -0,0 +1,144 @@
+package org.schabi.newpipe.database
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import androidx.room.Room
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.schabi.newpipe.extractor.stream.StreamType
+
+@RunWith(AndroidJUnit4::class)
+class AppDatabaseTest {
+ companion object {
+ private const val DEFAULT_SERVICE_ID = 0
+ private const val DEFAULT_URL = "https://www.youtube.com/watch?v=cDphUib5iG4"
+ private const val DEFAULT_TITLE = "Test Title"
+ private val DEFAULT_TYPE = StreamType.VIDEO_STREAM
+ private const val DEFAULT_DURATION = 480L
+ private const val DEFAULT_UPLOADER_NAME = "Uploader Test"
+ private const val DEFAULT_THUMBNAIL = "https://example.com/example.jpg"
+
+ private const val DEFAULT_SECOND_SERVICE_ID = 0
+ private const val DEFAULT_SECOND_URL = "https://www.youtube.com/watch?v=ncQU6iBn5Fc"
+ }
+
+ @get:Rule
+ 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)
+ }
+ )
+ close()
+ }
+
+ testHelper.runMigrationsAndValidate(
+ AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
+ true, Migrations.MIGRATION_2_3
+ )
+
+ testHelper.runMigrationsAndValidate(
+ AppDatabase.DATABASE_NAME, Migrations.DB_VER_4,
+ true, Migrations.MIGRATION_3_4
+ )
+
+ testHelper.runMigrationsAndValidate(
+ AppDatabase.DATABASE_NAME, Migrations.DB_VER_5,
+ true, Migrations.MIGRATION_4_5
+ )
+
+ val migratedDatabaseV3 = getMigratedDatabase()
+ val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst()
+
+ // Only expect 2, the one with the null url will be ignored
+ assertEquals(2, listFromDB.size)
+
+ val streamFromMigratedDatabase = listFromDB[0]
+ assertEquals(DEFAULT_SERVICE_ID, streamFromMigratedDatabase.serviceId)
+ assertEquals(DEFAULT_URL, streamFromMigratedDatabase.url)
+ assertEquals(DEFAULT_TITLE, streamFromMigratedDatabase.title)
+ assertEquals(DEFAULT_TYPE, streamFromMigratedDatabase.streamType)
+ assertEquals(DEFAULT_DURATION, streamFromMigratedDatabase.duration)
+ assertEquals(DEFAULT_UPLOADER_NAME, streamFromMigratedDatabase.uploader)
+ assertEquals(DEFAULT_THUMBNAIL, streamFromMigratedDatabase.thumbnailUrl)
+ assertNull(streamFromMigratedDatabase.viewCount)
+ assertNull(streamFromMigratedDatabase.textualUploadDate)
+ assertNull(streamFromMigratedDatabase.uploadDate)
+ assertNull(streamFromMigratedDatabase.isUploadDateApproximation)
+
+ val secondStreamFromMigratedDatabase = listFromDB[1]
+ assertEquals(DEFAULT_SECOND_SERVICE_ID, secondStreamFromMigratedDatabase.serviceId)
+ assertEquals(DEFAULT_SECOND_URL, secondStreamFromMigratedDatabase.url)
+ assertEquals("", secondStreamFromMigratedDatabase.title)
+ // Should fallback to VIDEO_STREAM
+ assertEquals(StreamType.VIDEO_STREAM, secondStreamFromMigratedDatabase.streamType)
+ assertEquals(0, secondStreamFromMigratedDatabase.duration)
+ assertEquals("", secondStreamFromMigratedDatabase.uploader)
+ assertEquals("", secondStreamFromMigratedDatabase.thumbnailUrl)
+ assertNull(secondStreamFromMigratedDatabase.viewCount)
+ assertNull(secondStreamFromMigratedDatabase.textualUploadDate)
+ assertNull(secondStreamFromMigratedDatabase.uploadDate)
+ assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation)
+ }
+
+ private fun getMigratedDatabase(): AppDatabase {
+ val database: AppDatabase = Room.databaseBuilder(
+ ApplicationProvider.getApplicationContext(),
+ AppDatabase::class.java, AppDatabase.DATABASE_NAME
+ )
+ .build()
+ testHelper.closeWhenFinished(database)
+ return database
+ }
+}
From a5b9fe4c352bf7345e52d646a8b24795c1a02ba5 Mon Sep 17 00:00:00 2001
From: Koitharu
Date: Tue, 20 Jul 2021 13:20:51 +0300
Subject: [PATCH 004/213] Refactor FeedLoadService to use it within the
notification worker
---
app/build.gradle | 4 +-
.../settings/DebugSettingsFragment.java | 9 +
app/src/main/java/org/schabi/newpipe/App.java | 19 +-
.../java/org/schabi/newpipe/MainActivity.java | 2 +-
.../newpipe/database/stream/dao/StreamDAO.kt | 2 +-
.../list/channel/ChannelFragment.java | 8 +-
.../newpipe/local/feed/FeedDatabaseManager.kt | 4 +
.../feed/notifications/NotificationHelper.kt | 135 +++++++
.../feed/notifications/NotificationIcon.kt | 48 +++
.../feed}/notifications/NotificationWorker.kt | 43 ++-
.../feed}/notifications/ScheduleOptions.kt | 2 +-
.../local/feed/service/FeedLoadManager.kt | 217 ++++++++++++
.../local/feed/service/FeedLoadService.kt | 329 +++---------------
.../local/feed/service/FeedLoadState.kt | 7 +
.../local/feed/service/FeedResultsHolder.kt | 19 +
.../local/feed/service/FeedUpdateInfo.kt | 34 ++
.../newpipe/notifications/ChannelUpdates.kt | 46 ---
.../notifications/NotificationHelper.java | 137 --------
.../notifications/NotificationIcon.java | 60 ----
.../notifications/SubscriptionUpdates.kt | 53 ---
.../settings/NotificationsSettingsFragment.kt | 8 +-
.../drawable-anydpi-v24/ic_stat_newpipe.xml | 14 -
.../res/drawable-hdpi/ic_stat_newpipe.png | Bin 413 -> 0 bytes
.../res/drawable-mdpi/ic_stat_newpipe.png | Bin 294 -> 0 bytes
.../res/drawable-xhdpi/ic_stat_newpipe.png | Bin 522 -> 0 bytes
.../res/drawable-xxhdpi/ic_stat_newpipe.png | Bin 731 -> 0 bytes
app/src/main/res/values/settings_keys.xml | 1 +
app/src/main/res/values/strings.xml | 1 +
app/src/main/res/xml/debug_settings.xml | 5 +
29 files changed, 576 insertions(+), 631 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
rename app/src/main/java/org/schabi/newpipe/{ => local/feed}/notifications/NotificationWorker.kt (64%)
rename app/src/main/java/org/schabi/newpipe/{ => local/feed}/notifications/ScheduleOptions.kt (96%)
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadState.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/service/FeedResultsHolder.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt
delete mode 100644 app/src/main/java/org/schabi/newpipe/notifications/ChannelUpdates.kt
delete mode 100644 app/src/main/java/org/schabi/newpipe/notifications/NotificationHelper.java
delete mode 100644 app/src/main/java/org/schabi/newpipe/notifications/NotificationIcon.java
delete mode 100644 app/src/main/java/org/schabi/newpipe/notifications/SubscriptionUpdates.kt
delete mode 100644 app/src/main/res/drawable-anydpi-v24/ic_stat_newpipe.xml
delete mode 100644 app/src/main/res/drawable-hdpi/ic_stat_newpipe.png
delete mode 100644 app/src/main/res/drawable-mdpi/ic_stat_newpipe.png
delete mode 100644 app/src/main/res/drawable-xhdpi/ic_stat_newpipe.png
delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_stat_newpipe.png
diff --git a/app/build.gradle b/app/build.gradle
index 61a0cdc2b..a9a4e7001 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -214,8 +214,8 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.webkit:webkit:1.4.0'
implementation 'com.google.android.material:material:1.2.1'
- implementation "androidx.work:work-runtime:${workVersion}"
- implementation "androidx.work:work-rxjava2:${workVersion}"
+ implementation "androidx.work:work-runtime-ktx:${workVersion}"
+ implementation "androidx.work:work-rxjava3:${workVersion}"
/** Third-party libraries **/
// Instance state boilerplate elimination
diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
index 55b2c7708..31c8dd40c 100644
--- a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
+++ b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java
@@ -6,6 +6,7 @@ import androidx.preference.Preference;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.PicassoHelper;
+import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
import leakcanary.LeakCanary;
@@ -20,10 +21,13 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
= findPreference(getString(R.string.show_image_indicators_key));
final Preference crashTheAppPreference
= findPreference(getString(R.string.crash_the_app_key));
+ final Preference checkNewStreamsPreference
+ = findPreference(getString(R.string.check_new_streams_key));
assert showMemoryLeaksPreference != null;
assert showImageIndicatorsPreference != null;
assert crashTheAppPreference != null;
+ assert checkNewStreamsPreference != null;
showMemoryLeaksPreference.setOnPreferenceClickListener(preference -> {
startActivity(LeakCanary.INSTANCE.newLeakDisplayActivityIntent());
@@ -38,5 +42,10 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
crashTheAppPreference.setOnPreferenceClickListener(preference -> {
throw new RuntimeException();
});
+
+ checkNewStreamsPreference.setOnPreferenceClickListener(preference -> {
+ NotificationWorker.runNow(preference.getContext());
+ return true;
+ });
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 67b7b2527..3c2866c94 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -1,7 +1,5 @@
package org.schabi.newpipe;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
@@ -250,18 +248,15 @@ public class App extends MultiDexApplication {
.setDescription(getString(R.string.hash_channel_description))
.build();
- final NotificationChannel newStreamsChannel = new NotificationChannel(
- getString(R.string.streams_notification_channel_id),
- getString(R.string.streams_notification_channel_name),
- NotificationManager.IMPORTANCE_DEFAULT
- );
- newStreamsChannel.setDescription(
- getString(R.string.streams_notification_channel_description)
- );
- newStreamsChannel.enableVibration(false);
+ final NotificationChannelCompat newStreamsChannel = new NotificationChannelCompat
+ .Builder(getString(R.string.streams_notification_channel_id),
+ NotificationManagerCompat.IMPORTANCE_DEFAULT)
+ .setName(getString(R.string.streams_notification_channel_name))
+ .setDescription(getString(R.string.streams_notification_channel_description))
+ .build();
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
- notificationManager.createNotificationChannels(
+ notificationManager.createNotificationChannelsCompat(
Arrays.asList(mainChannel, appUpdateChannel, hashChannel, newStreamsChannel)
);
}
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 7770d25dc..89964acbc 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -69,7 +69,7 @@ import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
-import org.schabi.newpipe.notifications.NotificationWorker;
+import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.event.OnKeyDownListener;
import org.schabi.newpipe.player.helper.PlayerHolder;
diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt
index fbb46134d..a22fd2bb9 100644
--- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt
+++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt
@@ -40,7 +40,7 @@ abstract class StreamDAO : BasicDAO {
internal abstract fun silentInsertAllInternal(streams: List): List
@Query("SELECT COUNT(*) != 0 FROM streams WHERE url = :url AND service_id = :serviceId")
- internal abstract fun exists(serviceId: Long, url: String?): Boolean
+ internal abstract fun exists(serviceId: Int, url: String): Boolean
@Query(
"""
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index 754036dfd..941035b07 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -44,7 +44,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
-import org.schabi.newpipe.notifications.NotificationHelper;
+import org.schabi.newpipe.local.feed.notifications.NotificationHelper;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -252,13 +252,13 @@ public class ChannelFragment extends BaseListInfoFragment
.map(List::isEmpty)
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
- .subscribe((Boolean isEmpty) -> updateSubscribeButton(!isEmpty), onError));
+ .subscribe(isEmpty -> updateSubscribeButton(!isEmpty), onError));
disposables.add(observable
.map(List::isEmpty)
- .filter(x -> NotificationHelper.isNewStreamsNotificationsEnabled(requireContext()))
.distinctUntilChanged()
- .skip(1)
+ .skip(1) // channel has just been opened
+ .filter(x -> NotificationHelper.isNewStreamsNotificationsEnabled(requireContext()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(isEmpty -> {
if (!isEmpty) {
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
index ff7c2848e..c4a9f6af9 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
@@ -72,6 +72,10 @@ class FeedDatabaseManager(context: Context) {
fun markAsOutdated(subscriptionId: Long) = feedTable
.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
+ fun isStreamExist(stream: StreamInfoItem): Boolean {
+ return streamTable.exists(stream.serviceId, stream.url)
+ }
+
fun upsertAll(
subscriptionId: Long,
items: List,
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
new file mode 100644
index 000000000..ec5cb790f
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -0,0 +1,135 @@
+package org.schabi.newpipe.local.feed.notifications
+
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.net.Uri
+import android.os.Build
+import android.provider.Settings
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.stream.StreamInfoItem
+import org.schabi.newpipe.local.feed.service.FeedUpdateInfo
+import org.schabi.newpipe.util.NavigationHelper
+
+class NotificationHelper(val context: Context) {
+
+ private val manager = context.getSystemService(
+ Context.NOTIFICATION_SERVICE
+ ) as NotificationManager
+
+ fun notify(data: FeedUpdateInfo): Completable {
+ val newStreams: List = data.newStreams
+ val summary = context.resources.getQuantityString(
+ R.plurals.new_streams, newStreams.size, newStreams.size
+ )
+ val builder = NotificationCompat.Builder(
+ context,
+ context.getString(R.string.streams_notification_channel_id)
+ )
+ .setContentTitle(
+ context.getString(
+ R.string.notification_title_pattern,
+ data.name,
+ summary
+ )
+ )
+ .setContentText(
+ data.listInfo.relatedItems.joinToString(
+ context.getString(R.string.enumeration_comma)
+ ) { x -> x.name }
+ )
+ .setNumber(newStreams.size)
+ .setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
+ .setLargeIcon(
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.ic_newpipe_triangle_white
+ )
+ )
+ .setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
+ .setColorized(true)
+ .setAutoCancel(true)
+ .setCategory(NotificationCompat.CATEGORY_SOCIAL)
+ val style = NotificationCompat.InboxStyle()
+ for (stream in newStreams) {
+ style.addLine(stream.name)
+ }
+ style.setSummaryText(summary)
+ style.setBigContentTitle(data.name)
+ builder.setStyle(style)
+ builder.setContentIntent(
+ PendingIntent.getActivity(
+ context,
+ data.pseudoId,
+ NavigationHelper.getChannelIntent(context, data.listInfo.serviceId, data.listInfo.url)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ 0
+ )
+ )
+ return Single.create(NotificationIcon(context, data.avatarUrl))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnSuccess { icon ->
+ builder.setLargeIcon(icon)
+ }
+ .ignoreElement()
+ .onErrorComplete()
+ .doOnComplete { manager.notify(data.pseudoId, builder.build()) }
+ }
+
+ companion object {
+ /**
+ * Check whether notifications are not disabled by user via system settings.
+ *
+ * @param context Context
+ * @return true if notifications are allowed, false otherwise
+ */
+ fun isNotificationsEnabledNative(context: Context): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channelId = context.getString(R.string.streams_notification_channel_id)
+ val manager = context.getSystemService(
+ Context.NOTIFICATION_SERVICE
+ ) as NotificationManager
+ val channel = manager.getNotificationChannel(channelId)
+ channel != null && channel.importance != NotificationManager.IMPORTANCE_NONE
+ } else {
+ NotificationManagerCompat.from(context).areNotificationsEnabled()
+ }
+ }
+
+ @JvmStatic
+ fun isNewStreamsNotificationsEnabled(context: Context): Boolean {
+ return (
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(context.getString(R.string.enable_streams_notifications), false) &&
+ isNotificationsEnabledNative(context)
+ )
+ }
+
+ fun openNativeSettingsScreen(context: Context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channelId = context.getString(R.string.streams_notification_channel_id)
+ val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
+ .putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
+ context.startActivity(intent)
+ } else {
+ val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ intent.data = Uri.parse("package:" + context.packageName)
+ context.startActivity(intent)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
new file mode 100644
index 000000000..eea39dfd3
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
@@ -0,0 +1,48 @@
+package org.schabi.newpipe.local.feed.notifications
+
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.Bitmap
+import android.view.View
+import com.nostra13.universalimageloader.core.ImageLoader
+import com.nostra13.universalimageloader.core.assist.FailReason
+import com.nostra13.universalimageloader.core.assist.ImageSize
+import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener
+import io.reactivex.rxjava3.core.SingleEmitter
+import io.reactivex.rxjava3.core.SingleOnSubscribe
+
+internal class NotificationIcon(
+ context: Context,
+ private val url: String
+) : SingleOnSubscribe {
+
+ private val size = getIconSize(context)
+
+ override fun subscribe(emitter: SingleEmitter) {
+ ImageLoader.getInstance().loadImage(
+ url,
+ ImageSize(size, size),
+ object : SimpleImageLoadingListener() {
+ override fun onLoadingFailed(imageUri: String?, view: View?, failReason: FailReason) {
+ emitter.onError(failReason.cause)
+ }
+
+ override fun onLoadingComplete(imageUri: String?, view: View?, loadedImage: Bitmap) {
+ emitter.onSuccess(loadedImage)
+ }
+ }
+ )
+ }
+
+ private companion object {
+
+ fun getIconSize(context: Context): Int {
+ val activityManager = context.getSystemService(
+ Context.ACTIVITY_SERVICE
+ ) as ActivityManager?
+ val size1 = activityManager?.launcherLargeIconSize ?: 0
+ val size2 = context.resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
+ return maxOf(size2, size1)
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
similarity index 64%
rename from app/src/main/java/org/schabi/newpipe/notifications/NotificationWorker.kt
rename to app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index 24dbc82e0..896735983 100644
--- a/app/src/main/java/org/schabi/newpipe/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.notifications
+package org.schabi.newpipe.local.feed.notifications
import android.content.Context
import androidx.preference.PreferenceManager
@@ -6,14 +6,16 @@ import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
+import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequest
-import androidx.work.RxWorker
import androidx.work.WorkManager
import androidx.work.WorkerParameters
-import io.reactivex.BackpressureStrategy
-import io.reactivex.Flowable
-import io.reactivex.Single
+import androidx.work.rxjava3.RxWorker
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.Single
import org.schabi.newpipe.R
+import org.schabi.newpipe.database.subscription.NotificationMode
+import org.schabi.newpipe.local.feed.service.FeedLoadManager
import java.util.concurrent.TimeUnit
class NotificationWorker(
@@ -24,20 +26,27 @@ class NotificationWorker(
private val notificationHelper by lazy {
NotificationHelper(appContext)
}
+ private val feedLoadManager = FeedLoadManager(appContext)
- override fun createWork() = if (isEnabled(applicationContext)) {
- Flowable.create(
- SubscriptionUpdates(applicationContext),
- BackpressureStrategy.BUFFER
- ).doOnNext { notificationHelper.notify(it) }
- .toList()
- .map { Result.success() }
+ override fun createWork(): Single = if (isEnabled(applicationContext)) {
+ feedLoadManager.startLoading()
+ .map { feed ->
+ feed.mapNotNull { x ->
+ x.value?.takeIf {
+ it.notificationMode == NotificationMode.ENABLED_DEFAULT &&
+ it.newStreamsCount > 0
+ }
+ }
+ }
+ .flatMapObservable { Observable.fromIterable(it) }
+ .flatMapCompletable { x -> notificationHelper.notify(x) }
+ .toSingleDefault(Result.success())
.onErrorReturnItem(Result.failure())
} else Single.just(Result.success())
companion object {
- private const val TAG = "notifications"
+ private const val TAG = "streams_notifications"
private fun isEnabled(context: Context): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context)
@@ -78,5 +87,13 @@ class NotificationWorker(
@JvmStatic
fun schedule(context: Context) = schedule(context, ScheduleOptions.from(context))
+
+ @JvmStatic
+ fun runNow(context: Context) {
+ val request = OneTimeWorkRequestBuilder()
+ .addTag(TAG)
+ .build()
+ WorkManager.getInstance(context).enqueue(request)
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/ScheduleOptions.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
similarity index 96%
rename from app/src/main/java/org/schabi/newpipe/notifications/ScheduleOptions.kt
rename to app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
index b0617b303..30e8d5515 100644
--- a/app/src/main/java/org/schabi/newpipe/notifications/ScheduleOptions.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.notifications
+package org.schabi.newpipe.local.feed.notifications
import android.content.Context
import androidx.preference.PreferenceManager
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
new file mode 100644
index 000000000..79c4b747b
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
@@ -0,0 +1,217 @@
+package org.schabi.newpipe.local.feed.service
+
+import android.content.Context
+import androidx.preference.PreferenceManager
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.core.Notification
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.functions.Consumer
+import io.reactivex.rxjava3.processors.PublishProcessor
+import io.reactivex.rxjava3.schedulers.Schedulers
+import org.schabi.newpipe.R
+import org.schabi.newpipe.database.feed.model.FeedGroupEntity
+import org.schabi.newpipe.extractor.ListInfo
+import org.schabi.newpipe.extractor.stream.StreamInfoItem
+import org.schabi.newpipe.local.feed.FeedDatabaseManager
+import org.schabi.newpipe.local.subscription.SubscriptionManager
+import org.schabi.newpipe.util.ExtractorHelper
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+
+class FeedLoadManager(private val context: Context) {
+
+ private val subscriptionManager = SubscriptionManager(context)
+ private val feedDatabaseManager = FeedDatabaseManager(context)
+
+ private val notificationUpdater = PublishProcessor.create()
+ private val currentProgress = AtomicInteger(-1)
+ private val maxProgress = AtomicInteger(-1)
+ private val cancelSignal = AtomicBoolean()
+ private val feedResultsHolder = FeedResultsHolder()
+
+ val notification: Flowable = notificationUpdater.map { description ->
+ FeedLoadState(description, maxProgress.get(), currentProgress.get())
+ }
+
+ fun startLoading(
+ groupId: Long = FeedGroupEntity.GROUP_ALL_ID
+ ): Single>> {
+ val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+ val useFeedExtractor = defaultSharedPreferences.getBoolean(
+ context.getString(R.string.feed_use_dedicated_fetch_method_key),
+ false
+ )
+ val thresholdOutdatedSeconds = defaultSharedPreferences.getString(
+ context.getString(R.string.feed_update_threshold_key),
+ context.getString(R.string.feed_update_threshold_default_value)
+ )!!.toInt()
+
+ val outdatedThreshold = OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong())
+
+ val subscriptions = when (groupId) {
+ FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold)
+ else -> feedDatabaseManager.outdatedSubscriptionsForGroup(groupId, outdatedThreshold)
+ }
+
+ return subscriptions
+ .take(1)
+
+ .doOnNext {
+ currentProgress.set(0)
+ maxProgress.set(it.size)
+ }
+ .filter { it.isNotEmpty() }
+
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnNext {
+ notificationUpdater.onNext("")
+ 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 ->
+ var error: Throwable? = null
+ try {
+ val listInfo = if (useFeedExtractor) {
+ ExtractorHelper
+ .getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
+ .onErrorReturn {
+ error = it // store error, otherwise wrapped into RuntimeException
+ throw it
+ }
+ .blockingGet()
+ } else {
+ ExtractorHelper
+ .getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
+ .onErrorReturn {
+ error = it // store error, otherwise wrapped into RuntimeException
+ throw it
+ }
+ .blockingGet()
+ } as ListInfo
+
+ return@map Notification.createOnNext(FeedUpdateInfo(subscriptionEntity, listInfo))
+ } catch (e: Throwable) {
+ if (error == null) {
+ // do this to prevent blockingGet() from wrapping into RuntimeException
+ error = e
+ }
+
+ val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
+ val wrapper = FeedLoadService.RequestException(subscriptionEntity.uid, request, error!!)
+ return@map Notification.createOnError(wrapper)
+ }
+ }
+ .sequential()
+
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnNext(NotificationConsumer())
+
+ .observeOn(Schedulers.io())
+ .buffer(BUFFER_COUNT_BEFORE_INSERT)
+ .doOnNext(DatabaseConsumer())
+
+ .subscribeOn(Schedulers.io())
+ .toList()
+ .flatMap { x -> postProcessFeed().toSingleDefault(x.flatten()) }
+ }
+
+ fun cancel() {
+ cancelSignal.set(true)
+ }
+
+ private fun broadcastProgress() {
+ FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(currentProgress.get(), maxProgress.get()))
+ }
+
+ private fun postProcessFeed() = Completable.fromRunnable {
+ FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message))
+ feedDatabaseManager.removeOrphansOrOlderStreams()
+
+ FeedEventManager.postEvent(FeedEventManager.Event.SuccessResultEvent(feedResultsHolder.itemsErrors))
+ }.doOnSubscribe {
+ currentProgress.set(-1)
+ maxProgress.set(-1)
+
+ notificationUpdater.onNext(context.getString(R.string.feed_processing_message))
+ FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message))
+ }.subscribeOn(Schedulers.io())
+
+ private inner class NotificationConsumer : Consumer> {
+ override fun accept(item: Notification) {
+ currentProgress.incrementAndGet()
+ notificationUpdater.onNext(item.value?.name.orEmpty())
+
+ broadcastProgress()
+ }
+ }
+
+ private inner class DatabaseConsumer : Consumer>> {
+
+ override fun accept(list: List>) {
+ feedDatabaseManager.database().runInTransaction {
+ for (notification in list) {
+ when {
+ notification.isOnNext -> {
+ val subscriptionId = notification.value.uid
+ val info = notification.value.listInfo
+
+ notification.value.newStreamsCount = countNewStreams(info.relatedItems)
+ feedDatabaseManager.upsertAll(subscriptionId, info.relatedItems)
+ subscriptionManager.updateFromInfo(subscriptionId, info)
+
+ if (info.errors.isNotEmpty()) {
+ feedResultsHolder.addErrors(FeedLoadService.RequestException.wrapList(subscriptionId, info))
+ feedDatabaseManager.markAsOutdated(subscriptionId)
+ }
+ }
+ notification.isOnError -> {
+ val error = notification.error
+ feedResultsHolder.addError(error)
+
+ if (error is FeedLoadService.RequestException) {
+ feedDatabaseManager.markAsOutdated(error.subscriptionId)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun countNewStreams(list: List): Int {
+ var count = 0
+ for (item in list) {
+ if (feedDatabaseManager.isStreamExist(item)) {
+ return count
+ } else {
+ count++
+ }
+ }
+ return 0
+ }
+ }
+
+ private companion object {
+
+ /**
+ * How many extractions will be running in parallel.
+ */
+ const val PARALLEL_EXTRACTIONS = 6
+
+ /**
+ * Number of items to buffer to mass-insert in the database.
+ */
+ const val BUFFER_COUNT_BEFORE_INSERT = 20
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
index 98ff5914d..ea181d3d9 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
@@ -31,36 +31,19 @@ import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
-import androidx.preference.PreferenceManager
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.disposables.Disposable
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.App
import org.schabi.newpipe.MainActivity.DEBUG
import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.extractor.ListInfo
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.ProgressEvent
-import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.SuccessResultEvent
import org.schabi.newpipe.local.feed.service.FeedEventManager.postEvent
-import org.schabi.newpipe.local.subscription.SubscriptionManager
-import org.schabi.newpipe.util.ExtractorHelper
-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 {
@@ -73,27 +56,13 @@ class FeedLoadService : Service() {
*/
private const val NOTIFICATION_SAMPLING_PERIOD = 1500
- /**
- * How many extractions will be running in parallel.
- */
- private const val PARALLEL_EXTRACTIONS = 6
-
- /**
- * Number of items to buffer to mass-insert in the database.
- */
- private const val BUFFER_COUNT_BEFORE_INSERT = 20
-
const val EXTRA_GROUP_ID: String = "FeedLoadService.EXTRA_GROUP_ID"
}
- private var loadingSubscription: Subscription? = null
- private lateinit var subscriptionManager: SubscriptionManager
+ private var loadingDisposable: Disposable? = null
+ private var notificationDisposable: Disposable? = null
- private lateinit var feedDatabaseManager: FeedDatabaseManager
- private lateinit var feedResultsHolder: ResultsHolder
-
- private var disposables = CompositeDisposable()
- private var notificationUpdater = PublishProcessor.create()
+ private lateinit var feedLoadManager: FeedLoadManager
// /////////////////////////////////////////////////////////////////////////
// Lifecycle
@@ -101,8 +70,7 @@ class FeedLoadService : Service() {
override fun onCreate() {
super.onCreate()
- subscriptionManager = SubscriptionManager(this)
- feedDatabaseManager = FeedDatabaseManager(this)
+ feedLoadManager = FeedLoadManager(this)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -114,40 +82,45 @@ class FeedLoadService : Service() {
)
}
- if (intent == null || loadingSubscription != null) {
+ if (intent == null || loadingDisposable != null) {
return START_NOT_STICKY
}
setupNotification()
setupBroadcastReceiver()
- val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
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)
-
- val thresholdOutdatedSecondsString = defaultSharedPreferences
- .getString(getString(R.string.feed_update_threshold_key), getString(R.string.feed_update_threshold_default_value))
- val thresholdOutdatedSeconds = thresholdOutdatedSecondsString!!.toInt()
-
- startLoading(groupId, useFeedExtractor, thresholdOutdatedSeconds)
-
+ loadingDisposable = feedLoadManager.startLoading(groupId)
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnSubscribe {
+ startForeground(NOTIFICATION_ID, notificationBuilder.build())
+ }
+ .subscribe { _, error ->
+ // There seems to be a bug in the kotlin plugin as it tells you when
+ // building that this can't be null:
+ // "Condition 'error != null' is always 'true'"
+ // However it can indeed be null
+ // The suppression may be removed in further versions
+ @Suppress("SENSELESS_COMPARISON")
+ if (error != null) {
+ Log.e(TAG, "Error while storing result", error)
+ handleError(error)
+ return@subscribe
+ }
+ stopService()
+ }
return START_NOT_STICKY
}
private fun disposeAll() {
unregisterReceiver(broadcastReceiver)
-
- loadingSubscription?.cancel()
- loadingSubscription = null
-
- disposables.dispose()
+ loadingDisposable?.dispose()
+ notificationDisposable?.dispose()
}
private fun stopService() {
disposeAll()
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
- notificationManager.cancel(NOTIFICATION_ID)
stopSelf()
}
@@ -171,190 +144,6 @@ class FeedLoadService : Service() {
}
}
- private fun startLoading(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, useFeedExtractor: Boolean, thresholdOutdatedSeconds: Int) {
- feedResultsHolder = ResultsHolder()
-
- val outdatedThreshold = OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong())
-
- val subscriptions = when (groupId) {
- FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold)
- else -> feedDatabaseManager.outdatedSubscriptionsForGroup(groupId, outdatedThreshold)
- }
-
- subscriptions
- .take(1)
-
- .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 ->
- var error: Throwable? = null
- try {
- val listInfo = if (useFeedExtractor) {
- ExtractorHelper
- .getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
- .onErrorReturn {
- error = it // store error, otherwise wrapped into RuntimeException
- throw it
- }
- .blockingGet()
- } else {
- ExtractorHelper
- .getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
- .onErrorReturn {
- error = it // store error, otherwise wrapped into RuntimeException
- throw it
- }
- .blockingGet()
- } as ListInfo
-
- return@map Notification.createOnNext(Pair(subscriptionEntity.uid, listInfo))
- } catch (e: Throwable) {
- if (error == null) {
- // do this to prevent blockingGet() from wrapping into RuntimeException
- error = e
- }
-
- val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
- val wrapper = RequestException(subscriptionEntity.uid, request, error!!)
- return@map Notification.createOnError>>(wrapper)
- }
- }
- .sequential()
-
- .observeOn(AndroidSchedulers.mainThread())
- .doOnNext(notificationsConsumer)
-
- .observeOn(Schedulers.io())
- .buffer(BUFFER_COUNT_BEFORE_INSERT)
- .doOnNext(databaseConsumer)
-
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(resultSubscriber)
- }
-
- private fun broadcastProgress() {
- postEvent(ProgressEvent(currentProgress.get(), maxProgress.get()))
- }
-
- private val resultSubscriber
- get() = object : Subscriber>>>> {
-
- override fun onSubscribe(s: Subscription) {
- loadingSubscription = s
- s.request(java.lang.Long.MAX_VALUE)
- }
-
- override fun onNext(notification: List>>>) {
- if (DEBUG) Log.v(TAG, "onNext() → $notification")
- }
-
- override fun onError(error: Throwable) {
- handleError(error)
- }
-
- override fun onComplete() {
- if (maxProgress.get() == 0) {
- postEvent(FeedEventManager.Event.IdleEvent)
- stopService()
-
- return
- }
-
- currentProgress.set(-1)
- maxProgress.set(-1)
-
- notificationUpdater.onNext(getString(R.string.feed_processing_message))
- postEvent(ProgressEvent(R.string.feed_processing_message))
-
- disposables.add(
- Single
- .fromCallable {
- feedResultsHolder.ready()
-
- postEvent(ProgressEvent(R.string.feed_processing_message))
- feedDatabaseManager.removeOrphansOrOlderStreams()
-
- postEvent(SuccessResultEvent(feedResultsHolder.itemsErrors))
- true
- }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe { _, throwable ->
- // There seems to be a bug in the kotlin plugin as it tells you when
- // building that this can't be null:
- // "Condition 'throwable != null' is always 'true'"
- // However it can indeed be null
- // The suppression may be removed in further versions
- @Suppress("SENSELESS_COMPARISON")
- if (throwable != null) {
- Log.e(TAG, "Error while storing result", throwable)
- handleError(throwable)
- return@subscribe
- }
- stopService()
- }
- )
- }
- }
-
- private val databaseConsumer: Consumer>>>>
- get() = Consumer {
- feedDatabaseManager.database().runInTransaction {
- for (notification in it) {
-
- if (notification.isOnNext) {
- val subscriptionId = notification.value!!.first
- val info = notification.value!!.second
-
- feedDatabaseManager.upsertAll(subscriptionId, info.relatedItems)
- subscriptionManager.updateFromInfo(subscriptionId, info)
-
- if (info.errors.isNotEmpty()) {
- feedResultsHolder.addErrors(RequestException.wrapList(subscriptionId, info))
- feedDatabaseManager.markAsOutdated(subscriptionId)
- }
- } else if (notification.isOnError) {
- val error = notification.error!!
- feedResultsHolder.addError(error)
-
- if (error is RequestException) {
- feedDatabaseManager.markAsOutdated(error.subscriptionId)
- }
- }
- }
- }
- }
-
- private val notificationsConsumer: Consumer>>>
- get() = Consumer { onItemCompleted(it.value?.second?.name) }
-
- private fun onItemCompleted(updateDescription: String?) {
- currentProgress.incrementAndGet()
- notificationUpdater.onNext(updateDescription ?: "")
-
- broadcastProgress()
- }
-
// /////////////////////////////////////////////////////////////////////////
// Notification
// /////////////////////////////////////////////////////////////////////////
@@ -362,9 +151,6 @@ class FeedLoadService : Service() {
private lateinit var notificationManager: NotificationManagerCompat
private lateinit var notificationBuilder: NotificationCompat.Builder
- private var currentProgress = AtomicInteger(-1)
- private var maxProgress = AtomicInteger(-1)
-
private fun createNotification(): NotificationCompat.Builder {
val cancelActionIntent = PendingIntent.getBroadcast(
this,
@@ -384,33 +170,36 @@ class FeedLoadService : Service() {
notificationManager = NotificationManagerCompat.from(this)
notificationBuilder = createNotification()
- val throttleAfterFirstEmission = Function { flow: Flowable ->
+ val throttleAfterFirstEmission = Function { flow: Flowable ->
flow.take(1).concatWith(flow.skip(1).throttleLatest(NOTIFICATION_SAMPLING_PERIOD.toLong(), TimeUnit.MILLISECONDS))
}
- disposables.add(
- notificationUpdater
- .publish(throttleAfterFirstEmission)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::updateNotificationProgress)
- )
+ notificationDisposable = feedLoadManager.notification
+ .publish(throttleAfterFirstEmission)
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnTerminate { notificationManager.cancel(NOTIFICATION_ID) }
+ .subscribe(this::updateNotificationProgress)
}
- private fun updateNotificationProgress(updateDescription: String?) {
- notificationBuilder.setProgress(maxProgress.get(), currentProgress.get(), maxProgress.get() == -1)
+ private fun updateNotificationProgress(state: FeedLoadState) {
+ notificationBuilder.setProgress(state.maxProgress, state.currentProgress, state.maxProgress == -1)
- if (maxProgress.get() == -1) {
+ if (state.maxProgress == -1) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) notificationBuilder.setContentInfo(null)
- if (!updateDescription.isNullOrEmpty()) notificationBuilder.setContentText(updateDescription)
- notificationBuilder.setContentText(updateDescription)
+ if (state.updateDescription.isNotEmpty()) notificationBuilder.setContentText(state.updateDescription)
+ notificationBuilder.setContentText(state.updateDescription)
} else {
- val progressText = this.currentProgress.toString() + "/" + maxProgress
+ val progressText = state.currentProgress.toString() + "/" + state.maxProgress
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (!updateDescription.isNullOrEmpty()) notificationBuilder.setContentText("$updateDescription ($progressText)")
+ if (state.updateDescription.isNotEmpty()) {
+ notificationBuilder.setContentText("${state.updateDescription} ($progressText)")
+ }
} else {
notificationBuilder.setContentInfo(progressText)
- if (!updateDescription.isNullOrEmpty()) notificationBuilder.setContentText(updateDescription)
+ if (state.updateDescription.isNotEmpty()) {
+ notificationBuilder.setContentText(state.updateDescription)
+ }
}
}
@@ -422,13 +211,12 @@ class FeedLoadService : Service() {
// /////////////////////////////////////////////////////////////////////////
private lateinit var broadcastReceiver: BroadcastReceiver
- private val cancelSignal = AtomicBoolean()
private fun setupBroadcastReceiver() {
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == ACTION_CANCEL) {
- cancelSignal.set(true)
+ feedLoadManager.cancel()
}
}
}
@@ -443,29 +231,4 @@ class FeedLoadService : Service() {
postEvent(ErrorResultEvent(error))
stopService()
}
-
- // /////////////////////////////////////////////////////////////////////////
- // Results Holder
- // /////////////////////////////////////////////////////////////////////////
-
- class ResultsHolder {
- /**
- * List of errors that may have happen during loading.
- */
- internal lateinit var itemsErrors: List
-
- private val itemsErrorsHolder: MutableList = ArrayList()
-
- fun addError(error: Throwable) {
- itemsErrorsHolder.add(error)
- }
-
- fun addErrors(errors: List) {
- itemsErrorsHolder.addAll(errors)
- }
-
- fun ready() {
- itemsErrors = itemsErrorsHolder.toList()
- }
- }
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadState.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadState.kt
new file mode 100644
index 000000000..703f593ad
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadState.kt
@@ -0,0 +1,7 @@
+package org.schabi.newpipe.local.feed.service
+
+data class FeedLoadState(
+ val updateDescription: String,
+ val maxProgress: Int,
+ val currentProgress: Int,
+)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedResultsHolder.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedResultsHolder.kt
new file mode 100644
index 000000000..729f2c009
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedResultsHolder.kt
@@ -0,0 +1,19 @@
+package org.schabi.newpipe.local.feed.service
+
+class FeedResultsHolder {
+ /**
+ * List of errors that may have happen during loading.
+ */
+ val itemsErrors: List
+ get() = itemsErrorsHolder
+
+ private val itemsErrorsHolder: MutableList = ArrayList()
+
+ fun addError(error: Throwable) {
+ itemsErrorsHolder.add(error)
+ }
+
+ fun addErrors(errors: List) {
+ itemsErrorsHolder.addAll(errors)
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt
new file mode 100644
index 000000000..a86578e15
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedUpdateInfo.kt
@@ -0,0 +1,34 @@
+package org.schabi.newpipe.local.feed.service
+
+import org.schabi.newpipe.database.subscription.NotificationMode
+import org.schabi.newpipe.database.subscription.SubscriptionEntity
+import org.schabi.newpipe.extractor.ListInfo
+import org.schabi.newpipe.extractor.stream.StreamInfoItem
+
+data class FeedUpdateInfo(
+ val uid: Long,
+ @NotificationMode
+ val notificationMode: Int,
+ val name: String,
+ val avatarUrl: String,
+ val listInfo: ListInfo
+) {
+ constructor(subscription: SubscriptionEntity, listInfo: ListInfo) : this(
+ uid = subscription.uid,
+ notificationMode = subscription.notificationMode,
+ name = subscription.name,
+ avatarUrl = subscription.avatarUrl,
+ listInfo = listInfo
+ )
+
+ /**
+ * Integer id, can be used as notification id, etc.
+ */
+ val pseudoId: Int
+ get() = listInfo.url.hashCode()
+
+ var newStreamsCount: Int = 0
+
+ val newStreams: List
+ get() = listInfo.relatedItems.take(newStreamsCount)
+}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/ChannelUpdates.kt b/app/src/main/java/org/schabi/newpipe/notifications/ChannelUpdates.kt
deleted file mode 100644
index 9a3b2cbf3..000000000
--- a/app/src/main/java/org/schabi/newpipe/notifications/ChannelUpdates.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.schabi.newpipe.notifications
-
-import android.content.Context
-import android.content.Intent
-import org.schabi.newpipe.R
-import org.schabi.newpipe.extractor.channel.ChannelInfo
-import org.schabi.newpipe.extractor.stream.StreamInfoItem
-import org.schabi.newpipe.util.NavigationHelper
-
-data class ChannelUpdates(
- val serviceId: Int,
- val url: String,
- val avatarUrl: String,
- val name: String,
- val streams: List
-) {
-
- val id = url.hashCode()
-
- val isNotEmpty: Boolean
- get() = streams.isNotEmpty()
-
- val size = streams.size
-
- fun getText(context: Context): String {
- val separator = context.resources.getString(R.string.enumeration_comma) + " "
- return streams.joinToString(separator) { it.name }
- }
-
- fun createOpenChannelIntent(context: Context?): Intent {
- return NavigationHelper.getChannelIntent(context, serviceId, url)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
-
- companion object {
- fun from(channel: ChannelInfo, streams: List): ChannelUpdates {
- return ChannelUpdates(
- channel.serviceId,
- channel.url,
- channel.avatarUrl,
- channel.name,
- streams
- )
- }
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/NotificationHelper.java b/app/src/main/java/org/schabi/newpipe/notifications/NotificationHelper.java
deleted file mode 100644
index 6207cd613..000000000
--- a/app/src/main/java/org/schabi/newpipe/notifications/NotificationHelper.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package org.schabi.newpipe.notifications;
-
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.Build;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.content.ContextCompat;
-import androidx.preference.PreferenceManager;
-
-import org.schabi.newpipe.BuildConfig;
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.stream.StreamInfoItem;
-
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
-public final class NotificationHelper {
-
- private final Context context;
- private final NotificationManager manager;
- private final CompositeDisposable disposable;
-
- public NotificationHelper(final Context context) {
- this.context = context;
- this.disposable = new CompositeDisposable();
- this.manager = (NotificationManager) context.getSystemService(
- Context.NOTIFICATION_SERVICE
- );
- }
-
- public Context getContext() {
- return context;
- }
-
- /**
- * Check whether notifications are not disabled by user via system settings.
- *
- * @param context Context
- * @return true if notifications are allowed, false otherwise
- */
- public static boolean isNotificationsEnabledNative(final Context context) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- final String channelId = context.getString(R.string.streams_notification_channel_id);
- final NotificationManager manager = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (manager != null) {
- final NotificationChannel channel = manager.getNotificationChannel(channelId);
- return channel != null
- && channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
- } else {
- return false;
- }
- } else {
- return NotificationManagerCompat.from(context).areNotificationsEnabled();
- }
- }
-
- public static boolean isNewStreamsNotificationsEnabled(@NonNull final Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context)
- .getBoolean(context.getString(R.string.enable_streams_notifications), false)
- && isNotificationsEnabledNative(context);
- }
-
- public static void openNativeSettingsScreen(final Context context) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- final String channelId = context.getString(R.string.streams_notification_channel_id);
- final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
- .putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
- .putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
- context.startActivity(intent);
- } else {
- final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- intent.setData(Uri.parse("package:" + context.getPackageName()));
- context.startActivity(intent);
- }
- }
-
- public void notify(final ChannelUpdates data) {
- final String summary = context.getResources().getQuantityString(
- R.plurals.new_streams, data.getSize(), data.getSize()
- );
- final NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
- context.getString(R.string.streams_notification_channel_id))
- .setContentTitle(
- context.getString(R.string.notification_title_pattern,
- data.getName(),
- summary)
- )
- .setContentText(data.getText(context))
- .setNumber(data.getSize())
- .setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
- .setPriority(NotificationCompat.PRIORITY_DEFAULT)
- .setSmallIcon(R.drawable.ic_stat_newpipe)
- .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_newpipe_triangle_white))
- .setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
- .setColorized(true)
- .setAutoCancel(true)
- .setCategory(NotificationCompat.CATEGORY_SOCIAL);
- final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
- for (final StreamInfoItem stream : data.getStreams()) {
- style.addLine(stream.getName());
- }
- style.setSummaryText(summary);
- style.setBigContentTitle(data.getName());
- builder.setStyle(style);
- builder.setContentIntent(PendingIntent.getActivity(
- context,
- data.getId(),
- data.createOpenChannelIntent(context),
- 0
- ));
-
- disposable.add(
- Single.create(new NotificationIcon(context, data.getAvatarUrl()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .doAfterTerminate(() -> manager.notify(data.getId(), builder.build()))
- .subscribe(builder::setLargeIcon, throwable -> {
- if (BuildConfig.DEBUG) {
- throwable.printStackTrace();
- }
- })
- );
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/NotificationIcon.java b/app/src/main/java/org/schabi/newpipe/notifications/NotificationIcon.java
deleted file mode 100644
index fc59b55f0..000000000
--- a/app/src/main/java/org/schabi/newpipe/notifications/NotificationIcon.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.schabi.newpipe.notifications;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.view.View;
-
-import com.nostra13.universalimageloader.core.ImageLoader;
-import com.nostra13.universalimageloader.core.assist.FailReason;
-import com.nostra13.universalimageloader.core.assist.ImageSize;
-import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
-
-import io.reactivex.rxjava3.annotations.NonNull;
-import io.reactivex.rxjava3.core.SingleEmitter;
-import io.reactivex.rxjava3.core.SingleOnSubscribe;
-
-final class NotificationIcon implements SingleOnSubscribe {
-
- private final String url;
- private final int size;
-
- NotificationIcon(final Context context, final String url) {
- this.url = url;
- this.size = getIconSize(context);
- }
-
- @Override
- public void subscribe(@NonNull final SingleEmitter emitter) throws Throwable {
- ImageLoader.getInstance().loadImage(
- url,
- new ImageSize(size, size),
- new SimpleImageLoadingListener() {
-
- @Override
- public void onLoadingFailed(final String imageUri,
- final View view,
- final FailReason failReason) {
- emitter.onError(failReason.getCause());
- }
-
- @Override
- public void onLoadingComplete(final String imageUri,
- final View view,
- final Bitmap loadedImage) {
- emitter.onSuccess(loadedImage);
- }
- }
- );
- }
-
- private static int getIconSize(final Context context) {
- final ActivityManager activityManager = (ActivityManager) context.getSystemService(
- Context.ACTIVITY_SERVICE
- );
- final int size2 = activityManager != null ? activityManager.getLauncherLargeIconSize() : 0;
- final int size1 = context.getResources()
- .getDimensionPixelSize(android.R.dimen.app_icon_size);
- return Math.max(size2, size1);
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/notifications/SubscriptionUpdates.kt b/app/src/main/java/org/schabi/newpipe/notifications/SubscriptionUpdates.kt
deleted file mode 100644
index 6f7c3881b..000000000
--- a/app/src/main/java/org/schabi/newpipe/notifications/SubscriptionUpdates.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.schabi.newpipe.notifications
-
-import android.content.Context
-import io.reactivex.FlowableEmitter
-import io.reactivex.FlowableOnSubscribe
-import org.schabi.newpipe.NewPipeDatabase
-import org.schabi.newpipe.database.stream.model.StreamEntity
-import org.schabi.newpipe.database.subscription.NotificationMode
-import org.schabi.newpipe.extractor.stream.StreamInfoItem
-import org.schabi.newpipe.local.subscription.SubscriptionManager
-import org.schabi.newpipe.util.ExtractorHelper
-
-class SubscriptionUpdates(context: Context) : FlowableOnSubscribe {
-
- private val subscriptionManager = SubscriptionManager(context)
- private val streamTable = NewPipeDatabase.getInstance(context).streamDAO()
-
- override fun subscribe(emitter: FlowableEmitter) {
- try {
- val subscriptions = subscriptionManager.subscriptions().blockingFirst()
- for (subscription in subscriptions) {
- if (subscription.notificationMode != NotificationMode.DISABLED) {
- val channel = ExtractorHelper.getChannelInfo(
- subscription.serviceId,
- subscription.url, true
- ).blockingGet()
- val updates = ChannelUpdates.from(channel, filterStreams(channel.relatedItems))
- if (updates.isNotEmpty) {
- emitter.onNext(updates)
- // prevent duplicated notifications
- streamTable.upsertAll(updates.streams.map { StreamEntity(it) })
- }
- }
- }
- emitter.onComplete()
- } catch (e: Exception) {
- emitter.onError(e)
- }
- }
-
- private fun filterStreams(list: List<*>): List {
- val streams = ArrayList(list.size)
- for (o in list) {
- if (o is StreamInfoItem) {
- if (streamTable.exists(o.serviceId.toLong(), o.url)) {
- break
- }
- streams.add(o)
- }
- }
- return streams
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
index 62a819e64..01a3ca6eb 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
@@ -14,10 +14,10 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity
import org.schabi.newpipe.error.ErrorActivity
import org.schabi.newpipe.error.ErrorInfo
import org.schabi.newpipe.error.UserAction
+import org.schabi.newpipe.local.feed.notifications.NotificationHelper
+import org.schabi.newpipe.local.feed.notifications.NotificationWorker
+import org.schabi.newpipe.local.feed.notifications.ScheduleOptions
import org.schabi.newpipe.local.subscription.SubscriptionManager
-import org.schabi.newpipe.notifications.NotificationHelper
-import org.schabi.newpipe.notifications.NotificationWorker
-import org.schabi.newpipe.notifications.ScheduleOptions
class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferenceChangeListener {
@@ -47,7 +47,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
override fun onResume() {
super.onResume()
- val enabled = NotificationHelper.isNotificationsEnabledNative(context)
+ val enabled = NotificationHelper.isNotificationsEnabledNative(requireContext())
preferenceScreen.isEnabled = enabled
if (!enabled) {
if (notificationWarningSnackbar == null) {
diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_newpipe.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_newpipe.xml
deleted file mode 100644
index e95f5b4ac..000000000
--- a/app/src/main/res/drawable-anydpi-v24/ic_stat_newpipe.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable-hdpi/ic_stat_newpipe.png b/app/src/main/res/drawable-hdpi/ic_stat_newpipe.png
deleted file mode 100644
index dc08d67ff89167b42ab133658c4a42d127aca7a5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 413
zcmV;O0b>4%P)QuB#Tw7U`%%oFC2aV032bBx#yy1-$Tux)hO3M%RC0Ua^Em
zD?$3XW-RMBuQXQ}vl0~Ar%RtEX?D?LB`CD-u>N3Inm4QExyFkl00000NkvXX
Hu0mjfv&*yy
diff --git a/app/src/main/res/drawable-mdpi/ic_stat_newpipe.png b/app/src/main/res/drawable-mdpi/ic_stat_newpipe.png
deleted file mode 100644
index 4af6c74df80b88fb6208df08da68e02ffbc98f94..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 294
zcmV+>0oneEP)igP)*Znnf&%5_L%L)@FOvc!@U8Y?i0hU39R)ID9
zFVA2CM70VyxddIB1)OYyTFnBKH!uqdwF)@71B03coa}=}%>tB9umDQ53OIQHBOphs
zfRkg;B2i!-95E%n!4fE!BoH&-4}*KA$P*X?d13_2m?AI--kBm7phJv+Yiu3Z@vuHN
zK(!bF*EK!hiYbuO8;{I0dcJPGlT`M=%OGD7>CE&{)kbnXhI+bCDfttAm%|ZsEbepMu+H96xPL(E?&$!iI=(v9wbBXQjrG_
z)*%tXgHhnYiXbWv5eWumNKl&F@56WC1Gm}3J3BV7zXzV%?hHIIGv9pgZV(Vc2qA>5
zkjv#R!way4HS!0#kz3(8tY?vb2Xg#@18^71C?g|n
z?NjY7JZ|b9s0@6p@@Op+xj&V
zKSYl?(6a13+xaU)jDcRjBY`zy2;^QWFRuAZ<$oIJ4BQ!5X00~u8Hj-xDC7y;$3Uz3
z08a($ojifuCVQ9rK~z9i1zNxVEG!Txxk+UT1JxutsU(CDLI^1ie*xq30c~$9$P@qo
N002ovPDHLkV1g)7OiKU&
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index 42d2233c8..efb91cbc5 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -188,6 +188,7 @@
disable_media_tunneling_key
crash_the_app_key
show_image_indicators_key
+ check_new_streams
theme
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 40dcd17c9..d8906200f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -481,6 +481,7 @@
Show image indicators
Show Picasso colored ribbons on top of images indicating their source: red for network, blue for disk and green for memory
Crash the app
+ Run check for new streams
Import
Import from
diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml
index 22abebcae..206401c23 100644
--- a/app/src/main/res/xml/debug_settings.xml
+++ b/app/src/main/res/xml/debug_settings.xml
@@ -49,6 +49,11 @@
android:title="@string/show_image_indicators_title"
app:iconSpaceReserved="false" />
+
+
Date: Sat, 4 Sep 2021 13:26:50 +0300
Subject: [PATCH 005/213] Migrate NotificationIcon to Picasso
---
.../5.json | 719 ++++++++++++++++++
.../feed/notifications/NotificationIcon.kt | 42 +-
.../schabi/newpipe/util/PicassoHelper.java | 9 +-
3 files changed, 751 insertions(+), 19 deletions(-)
create mode 100644 app/schemas/org.schabi.newpipe.database.AppDatabase/5.json
diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/5.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/5.json
new file mode 100644
index 000000000..9a1c62995
--- /dev/null
+++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/5.json
@@ -0,0 +1,719 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 5,
+ "identityHash": "096731b513bb71dd44517639f4a2c1e3",
+ "entities": [
+ {
+ "tableName": "subscriptions",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `name` TEXT, `avatar_url` TEXT, `subscriber_count` INTEGER, `description` TEXT, `notification_mode` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serviceId",
+ "columnName": "service_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "avatarUrl",
+ "columnName": "avatar_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "subscriberCount",
+ "columnName": "subscriber_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "notificationMode",
+ "columnName": "notification_mode",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_subscriptions_service_id_url",
+ "unique": true,
+ "columnNames": [
+ "service_id",
+ "url"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_subscriptions_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "search_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `creation_date` INTEGER, `service_id` INTEGER NOT NULL, `search` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "creationDate",
+ "columnName": "creation_date",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "serviceId",
+ "columnName": "service_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "search",
+ "columnName": "search",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_search_history_search",
+ "unique": false,
+ "columnNames": [
+ "search"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_search_history_search` ON `${TABLE_NAME}` (`search`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "streams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `stream_type` TEXT NOT NULL, `duration` INTEGER NOT NULL, `uploader` TEXT NOT NULL, `uploader_url` TEXT, `thumbnail_url` TEXT, `view_count` INTEGER, `textual_upload_date` TEXT, `upload_date` INTEGER, `is_upload_date_approximation` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serviceId",
+ "columnName": "service_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamType",
+ "columnName": "stream_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uploader",
+ "columnName": "uploader",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uploaderUrl",
+ "columnName": "uploader_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "thumbnailUrl",
+ "columnName": "thumbnail_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "viewCount",
+ "columnName": "view_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "textualUploadDate",
+ "columnName": "textual_upload_date",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "uploadDate",
+ "columnName": "upload_date",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isUploadDateApproximation",
+ "columnName": "is_upload_date_approximation",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_streams_service_id_url",
+ "unique": true,
+ "columnNames": [
+ "service_id",
+ "url"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_streams_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "stream_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, `repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "streamUid",
+ "columnName": "stream_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accessDate",
+ "columnName": "access_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatCount",
+ "columnName": "repeat_count",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "stream_id",
+ "access_date"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_stream_history_stream_id",
+ "unique": false,
+ "columnNames": [
+ "stream_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_stream_history_stream_id` ON `${TABLE_NAME}` (`stream_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "streams",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "stream_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "stream_state",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "streamUid",
+ "columnName": "stream_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "progressMillis",
+ "columnName": "progress_time",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "stream_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": [
+ {
+ "table": "streams",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "stream_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "playlists",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `thumbnail_url` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "thumbnailUrl",
+ "columnName": "thumbnail_url",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_playlists_name",
+ "unique": false,
+ "columnNames": [
+ "name"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_playlists_name` ON `${TABLE_NAME}` (`name`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "playlist_stream_join",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `stream_id` INTEGER NOT NULL, `join_index` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `join_index`), FOREIGN KEY(`playlist_id`) REFERENCES `playlists`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+ "fields": [
+ {
+ "fieldPath": "playlistUid",
+ "columnName": "playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "streamUid",
+ "columnName": "stream_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "index",
+ "columnName": "join_index",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "playlist_id",
+ "join_index"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_playlist_stream_join_playlist_id_join_index",
+ "unique": true,
+ "columnNames": [
+ "playlist_id",
+ "join_index"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_playlist_stream_join_playlist_id_join_index` ON `${TABLE_NAME}` (`playlist_id`, `join_index`)"
+ },
+ {
+ "name": "index_playlist_stream_join_stream_id",
+ "unique": false,
+ "columnNames": [
+ "stream_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_playlist_stream_join_stream_id` ON `${TABLE_NAME}` (`stream_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "playlists",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "playlist_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ },
+ {
+ "table": "streams",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "stream_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "remote_playlists",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, `thumbnail_url` TEXT, `uploader` TEXT, `stream_count` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serviceId",
+ "columnName": "service_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "thumbnailUrl",
+ "columnName": "thumbnail_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "uploader",
+ "columnName": "uploader",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "streamCount",
+ "columnName": "stream_count",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_remote_playlists_name",
+ "unique": false,
+ "columnNames": [
+ "name"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_remote_playlists_name` ON `${TABLE_NAME}` (`name`)"
+ },
+ {
+ "name": "index_remote_playlists_service_id_url",
+ "unique": true,
+ "columnNames": [
+ "service_id",
+ "url"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_remote_playlists_service_id_url` ON `${TABLE_NAME}` (`service_id`, `url`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "feed",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stream_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `subscription_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+ "fields": [
+ {
+ "fieldPath": "streamId",
+ "columnName": "stream_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subscriptionId",
+ "columnName": "subscription_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "stream_id",
+ "subscription_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_feed_subscription_id",
+ "unique": false,
+ "columnNames": [
+ "subscription_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "streams",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "stream_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ },
+ {
+ "table": "subscriptions",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "subscription_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "feed_group",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL, `sort_order` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "icon_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sortOrder",
+ "columnName": "sort_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_feed_group_sort_order",
+ "unique": false,
+ "columnNames": [
+ "sort_order"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_sort_order` ON `${TABLE_NAME}` (`sort_order`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "feed_group_subscription_join",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`group_id` INTEGER NOT NULL, `subscription_id` INTEGER NOT NULL, PRIMARY KEY(`group_id`, `subscription_id`), FOREIGN KEY(`group_id`) REFERENCES `feed_group`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+ "fields": [
+ {
+ "fieldPath": "feedGroupId",
+ "columnName": "group_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subscriptionId",
+ "columnName": "subscription_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "group_id",
+ "subscription_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_feed_group_subscription_join_subscription_id",
+ "unique": false,
+ "columnNames": [
+ "subscription_id"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_group_subscription_join_subscription_id` ON `${TABLE_NAME}` (`subscription_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "feed_group",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "group_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ },
+ {
+ "table": "subscriptions",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "subscription_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "feed_last_updated",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subscription_id` INTEGER NOT NULL, `last_updated` INTEGER, PRIMARY KEY(`subscription_id`), FOREIGN KEY(`subscription_id`) REFERENCES `subscriptions`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+ "fields": [
+ {
+ "fieldPath": "subscriptionId",
+ "columnName": "subscription_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastUpdated",
+ "columnName": "last_updated",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "subscription_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": [
+ {
+ "table": "subscriptions",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "subscription_id"
+ ],
+ "referencedColumns": [
+ "uid"
+ ]
+ }
+ ]
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '096731b513bb71dd44517639f4a2c1e3')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
index eea39dfd3..1073945d4 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
@@ -3,35 +3,43 @@ package org.schabi.newpipe.local.feed.notifications
import android.app.ActivityManager
import android.content.Context
import android.graphics.Bitmap
-import android.view.View
-import com.nostra13.universalimageloader.core.ImageLoader
-import com.nostra13.universalimageloader.core.assist.FailReason
-import com.nostra13.universalimageloader.core.assist.ImageSize
-import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener
+import android.graphics.drawable.Drawable
+import com.squareup.picasso.Picasso
+import com.squareup.picasso.Target
import io.reactivex.rxjava3.core.SingleEmitter
import io.reactivex.rxjava3.core.SingleOnSubscribe
+import org.schabi.newpipe.util.PicassoHelper
internal class NotificationIcon(
context: Context,
- private val url: String
+ private val url: String,
) : SingleOnSubscribe {
private val size = getIconSize(context)
override fun subscribe(emitter: SingleEmitter) {
- ImageLoader.getInstance().loadImage(
- url,
- ImageSize(size, size),
- object : SimpleImageLoadingListener() {
- override fun onLoadingFailed(imageUri: String?, view: View?, failReason: FailReason) {
- emitter.onError(failReason.cause)
- }
+ val target = SingleEmitterTarget(emitter)
+ PicassoHelper.loadThumbnail(url)
+ .resize(size, size)
+ .centerCrop()
+ .into(target)
+ emitter.setCancellable {
+ PicassoHelper.cancelRequest(target)
+ }
+ }
- override fun onLoadingComplete(imageUri: String?, view: View?, loadedImage: Bitmap) {
- emitter.onSuccess(loadedImage)
- }
+ private class SingleEmitterTarget(private val emitter: SingleEmitter) : Target {
+ override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom?) {
+ if (!emitter.isDisposed) {
+ emitter.onSuccess(bitmap)
}
- )
+ }
+
+ override fun onBitmapFailed(e: Exception, errorDrawable: Drawable?) {
+ emitter.tryOnError(e)
+ }
+
+ override fun onPrepareLoad(placeHolderDrawable: Drawable?) = Unit
}
private companion object {
diff --git a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java
index e15ecd277..efacd1fc2 100644
--- a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java
@@ -1,5 +1,7 @@
package org.schabi.newpipe.util;
+import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
+
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
@@ -9,6 +11,7 @@ import com.squareup.picasso.LruCache;
import com.squareup.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
+import com.squareup.picasso.Target;
import com.squareup.picasso.Transformation;
import org.schabi.newpipe.R;
@@ -19,8 +22,6 @@ import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
-import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
-
public final class PicassoHelper {
public static final String PLAYER_THUMBNAIL_TAG = "PICASSO_PLAYER_THUMBNAIL_TAG";
private static final String PLAYER_THUMBNAIL_TRANSFORMATION_KEY
@@ -78,6 +79,10 @@ public final class PicassoHelper {
picassoInstance.cancelTag(tag);
}
+ public static void cancelRequest(final Target target) {
+ picassoInstance.cancelRequest(target);
+ }
+
public static void setIndicatorsEnabled(final boolean enabled) {
picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging
}
From 111dc4963dab4de87ae08587b1dd44b6b986ab74 Mon Sep 17 00:00:00 2001
From: Koitharu
Date: Sat, 4 Sep 2021 14:53:11 +0300
Subject: [PATCH 006/213] Ignore feed update threshold when run from
NotificationWorker
---
.../feed/notifications/NotificationWorker.kt | 22 +++++++++++++++++--
.../local/feed/service/FeedLoadManager.kt | 19 +++++++++++-----
.../local/feed/service/FeedLoadService.kt | 2 +-
3 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index 896735983..ee16a403d 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -1,10 +1,12 @@
package org.schabi.newpipe.local.feed.notifications
import android.content.Context
+import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.ForegroundInfo
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequest
@@ -16,11 +18,12 @@ import io.reactivex.rxjava3.core.Single
import org.schabi.newpipe.R
import org.schabi.newpipe.database.subscription.NotificationMode
import org.schabi.newpipe.local.feed.service.FeedLoadManager
+import org.schabi.newpipe.local.feed.service.FeedLoadService
import java.util.concurrent.TimeUnit
class NotificationWorker(
appContext: Context,
- workerParams: WorkerParameters
+ workerParams: WorkerParameters,
) : RxWorker(appContext, workerParams) {
private val notificationHelper by lazy {
@@ -29,7 +32,7 @@ class NotificationWorker(
private val feedLoadManager = FeedLoadManager(appContext)
override fun createWork(): Single = if (isEnabled(applicationContext)) {
- feedLoadManager.startLoading()
+ feedLoadManager.startLoading(ignoreOutdatedThreshold = true)
.map { feed ->
feed.mapNotNull { x ->
x.value?.takeIf {
@@ -38,12 +41,27 @@ class NotificationWorker(
}
}
}
+ .doOnSubscribe { setForegroundAsync(createForegroundInfo()) }
.flatMapObservable { Observable.fromIterable(it) }
.flatMapCompletable { x -> notificationHelper.notify(x) }
.toSingleDefault(Result.success())
.onErrorReturnItem(Result.failure())
} else Single.just(Result.success())
+ private fun createForegroundInfo(): ForegroundInfo {
+ val notification = NotificationCompat.Builder(
+ applicationContext,
+ applicationContext.getString(R.string.notification_channel_id)
+ ).setOngoing(true)
+ .setProgress(-1, -1, true)
+ .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setContentTitle(applicationContext.getString(R.string.feed_notification_loading))
+ .build()
+ return ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification)
+ }
+
companion object {
private const val TAG = "streams_notifications"
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
index 79c4b747b..bea699999 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
@@ -38,19 +38,26 @@ class FeedLoadManager(private val context: Context) {
}
fun startLoading(
- groupId: Long = FeedGroupEntity.GROUP_ALL_ID
+ groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
+ ignoreOutdatedThreshold: Boolean = false,
): Single>> {
val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val useFeedExtractor = defaultSharedPreferences.getBoolean(
context.getString(R.string.feed_use_dedicated_fetch_method_key),
false
)
- val thresholdOutdatedSeconds = defaultSharedPreferences.getString(
- context.getString(R.string.feed_update_threshold_key),
- context.getString(R.string.feed_update_threshold_default_value)
- )!!.toInt()
- val outdatedThreshold = OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong())
+ val outdatedThreshold = if (ignoreOutdatedThreshold) {
+ OffsetDateTime.now(ZoneOffset.UTC)
+ } else {
+ val thresholdOutdatedSeconds = (
+ defaultSharedPreferences.getString(
+ context.getString(R.string.feed_update_threshold_key),
+ context.getString(R.string.feed_update_threshold_default_value)
+ ) ?: context.getString(R.string.feed_update_threshold_default_value)
+ ).toInt()
+ OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong())
+ }
val subscriptions = when (groupId) {
FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
index ea181d3d9..0d5d904e8 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt
@@ -48,7 +48,7 @@ import java.util.concurrent.TimeUnit
class FeedLoadService : Service() {
companion object {
private val TAG = FeedLoadService::class.java.simpleName
- private const val NOTIFICATION_ID = 7293450
+ const val NOTIFICATION_ID = 7293450
private const val ACTION_CANCEL = App.PACKAGE_NAME + ".local.feed.service.FeedLoadService.CANCEL"
/**
From 9d249904bdcd944bdae006f9e7d3a7ab6dec1285 Mon Sep 17 00:00:00 2001
From: Koitharu
Date: Sat, 4 Sep 2021 15:28:11 +0300
Subject: [PATCH 007/213] Toggle all subscriptions notification mode
---
.../settings/NotificationsSettingsFragment.kt | 7 +-
.../NotificationsChannelsConfigFragment.java | 84 -------------
.../NotificationsChannelsConfigFragment.kt | 112 ++++++++++++++++++
.../NotificationsConfigAdapter.kt | 2 +
app/src/main/res/drawable/ic_list_check.xml | 10 ++
.../res/menu/menu_notifications_channels.xml | 10 ++
app/src/main/res/values-ru/strings.xml | 1 +
app/src/main/res/values/strings.xml | 1 +
8 files changed, 137 insertions(+), 90 deletions(-)
delete mode 100644 app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.java
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
create mode 100644 app/src/main/res/drawable/ic_list_check.xml
create mode 100644 app/src/main/res/menu/menu_notifications_channels.xml
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
index 01a3ca6eb..50fb95450 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
@@ -87,12 +87,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
}
private fun updateSubscriptions(subscriptions: List) {
- var notified = 0
- for (subscription in subscriptions) {
- if (subscription.notificationMode != NotificationMode.DISABLED) {
- notified++
- }
- }
+ val notified = subscriptions.count { it.notificationMode != NotificationMode.DISABLED }
val preference = findPreference(getString(R.string.streams_notifications_channels_key))
if (preference != null) {
preference.summary = preference.context.getString(
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.java b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.java
deleted file mode 100644
index 7aa0826e5..000000000
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.schabi.newpipe.settings.notifications;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.database.subscription.NotificationMode;
-import org.schabi.newpipe.local.subscription.SubscriptionManager;
-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;
-
-public final class NotificationsChannelsConfigFragment extends Fragment
- implements NotificationsConfigAdapter.ModeToggleListener {
-
- private NotificationsConfigAdapter adapter;
- @Nullable
- private Disposable loader = null;
- private CompositeDisposable updaters;
-
- @Override
- public void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- adapter = new NotificationsConfigAdapter(this);
- updaters = new CompositeDisposable();
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull final LayoutInflater inflater,
- @Nullable final ViewGroup container,
- @Nullable final Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_channels_notifications, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- final RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
- recyclerView.setAdapter(adapter);
- }
-
- @Override
- public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (loader != null) {
- loader.dispose();
- }
- loader = new SubscriptionManager(requireContext())
- .subscriptions()
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(adapter::update);
- }
-
- @Override
- public void onDestroy() {
- if (loader != null) {
- loader.dispose();
- }
- updaters.dispose();
- super.onDestroy();
- }
-
- @Override
- public void onModeToggle(final int position, @NotificationMode final int mode) {
- final NotificationsConfigAdapter.SubscriptionItem subscription = adapter.getItem(position);
- updaters.add(
- new SubscriptionManager(requireContext())
- .updateNotificationMode(subscription.getServiceId(),
- subscription.getUrl(), mode)
- .subscribeOn(Schedulers.io())
- .subscribe()
- );
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
new file mode 100644
index 000000000..eb94fb843
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
@@ -0,0 +1,112 @@
+package org.schabi.newpipe.settings.notifications
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.RecyclerView
+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 org.schabi.newpipe.R
+import org.schabi.newpipe.database.subscription.NotificationMode
+import org.schabi.newpipe.local.subscription.SubscriptionManager
+import org.schabi.newpipe.settings.notifications.NotificationsConfigAdapter.ModeToggleListener
+
+class NotificationsChannelsConfigFragment : Fragment(), ModeToggleListener {
+
+ private lateinit var updaters: CompositeDisposable
+ private var loader: Disposable? = null
+ private var adapter: NotificationsConfigAdapter? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ updaters = CompositeDisposable()
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View = inflater.inflate(R.layout.fragment_channels_notifications, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view)
+ adapter = NotificationsConfigAdapter(this)
+ recyclerView.adapter = adapter
+ loader?.dispose()
+ loader = SubscriptionManager(requireContext())
+ .subscriptions()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe { newData -> adapter?.update(newData) }
+ }
+
+ override fun onDestroyView() {
+ loader?.dispose()
+ loader = null
+ super.onDestroyView()
+ }
+
+ override fun onDestroy() {
+ updaters.dispose()
+ super.onDestroy()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ inflater.inflate(R.menu.menu_notifications_channels, menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.action_toggle_all -> {
+ toggleAll()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onModeToggle(position: Int, @NotificationMode mode: Int) {
+ val subscription = adapter?.getItem(position) ?: return
+ updaters.add(
+ SubscriptionManager(requireContext())
+ .updateNotificationMode(
+ subscription.serviceId,
+ subscription.url,
+ mode
+ )
+ .subscribeOn(Schedulers.io())
+ .subscribe()
+ )
+ }
+
+ private fun toggleAll() {
+ val subscriptions = adapter?.getCurrentList() ?: return
+ val mode = subscriptions.firstOrNull()?.notificationMode ?: return
+ val newMode = when (mode) {
+ NotificationMode.DISABLED -> NotificationMode.ENABLED_DEFAULT
+ else -> NotificationMode.DISABLED
+ }
+ val subscriptionManager = SubscriptionManager(requireContext())
+ updaters.add(
+ CompositeDisposable(
+ subscriptions.map { item ->
+ subscriptionManager.updateNotificationMode(
+ serviceId = item.serviceId,
+ url = item.url,
+ mode = newMode
+ ).subscribeOn(Schedulers.io())
+ .subscribe()
+ }
+ )
+ )
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
index 44d2256af..1689747e2 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
@@ -40,6 +40,8 @@ class NotificationsConfigAdapter(
return differ.currentList[position].id
}
+ fun getCurrentList(): List = differ.currentList
+
fun update(newData: List) {
differ.submitList(
newData.map {
diff --git a/app/src/main/res/drawable/ic_list_check.xml b/app/src/main/res/drawable/ic_list_check.xml
new file mode 100644
index 000000000..37d806044
--- /dev/null
+++ b/app/src/main/res/drawable/ic_list_check.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_notifications_channels.xml b/app/src/main/res/menu/menu_notifications_channels.xml
new file mode 100644
index 000000000..79b9cd7c1
--- /dev/null
+++ b/app/src/main/res/menu/menu_notifications_channels.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 30821be51..0e29edcc4 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -705,4 +705,5 @@
Уведомления отключены
Уведомлять
Вы подписались на канал
+ Переключить все
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d8906200f..4da2ac60c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -730,4 +730,5 @@
,
%s • %s
%d/%d
+ Toggle all
\ No newline at end of file
From 55c51ad49d46e77b49382cd59cea81936d932f18 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Mon, 11 Oct 2021 23:20:52 +0200
Subject: [PATCH 008/213] Rename isStreamExist -> doesStreamExist
---
.../java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt | 2 +-
.../org/schabi/newpipe/local/feed/service/FeedLoadManager.kt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
index c4a9f6af9..996293225 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt
@@ -72,7 +72,7 @@ class FeedDatabaseManager(context: Context) {
fun markAsOutdated(subscriptionId: Long) = feedTable
.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
- fun isStreamExist(stream: StreamInfoItem): Boolean {
+ fun doesStreamExist(stream: StreamInfoItem): Boolean {
return streamTable.exists(stream.serviceId, stream.url)
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
index bea699999..6a44935c5 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
@@ -199,7 +199,7 @@ class FeedLoadManager(private val context: Context) {
private fun countNewStreams(list: List): Int {
var count = 0
for (item in list) {
- if (feedDatabaseManager.isStreamExist(item)) {
+ if (feedDatabaseManager.doesStreamExist(item)) {
return count
} else {
count++
From 7c6140b33100cb0cf877b83bc018fdab8018b863 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Fri, 15 Oct 2021 19:57:31 +0200
Subject: [PATCH 009/213] Remove unused code
---
.../org/schabi/newpipe/database/AppDatabaseTest.kt | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
index b80120074..158994cdd 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
+++ b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
@@ -44,7 +44,6 @@ class AppDatabaseTest {
insert(
"streams", SQLiteDatabase.CONFLICT_FAIL,
ContentValues().apply {
- // put("uid", null)
put("service_id", DEFAULT_SERVICE_ID)
put("url", DEFAULT_URL)
put("title", DEFAULT_TITLE)
@@ -57,27 +56,14 @@ class AppDatabaseTest {
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()
From 64a7978c7f44fc41647ad97e16a66043d72479a4 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Fri, 15 Oct 2021 19:59:06 +0200
Subject: [PATCH 010/213] Rename NotificationMode.ENABLED_DEFAULT to
NotificationMode.ENABLED
---
.../newpipe/database/subscription/NotificationMode.java | 4 ++--
.../newpipe/fragments/list/channel/ChannelFragment.java | 2 +-
.../newpipe/local/feed/notifications/NotificationWorker.kt | 2 +-
.../notifications/NotificationsChannelsConfigFragment.kt | 2 +-
.../settings/notifications/NotificationsConfigAdapter.kt | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java b/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java
index d817032ee..07e0eb7d3 100644
--- a/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java
+++ b/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java
@@ -4,11 +4,11 @@ import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-@IntDef({NotificationMode.DISABLED, NotificationMode.ENABLED_DEFAULT})
+@IntDef({NotificationMode.DISABLED, NotificationMode.ENABLED})
@Retention(RetentionPolicy.SOURCE)
public @interface NotificationMode {
int DISABLED = 0;
- int ENABLED_DEFAULT = 1;
+ int ENABLED = 1;
//other values reserved for the future
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index dcda6db0b..8ed6b5149 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -414,7 +414,7 @@ public class ChannelFragment extends BaseListInfoFragment
}
private void setNotify(final boolean isEnabled) {
- final int mode = isEnabled ? NotificationMode.ENABLED_DEFAULT : NotificationMode.DISABLED;
+ final int mode = isEnabled ? NotificationMode.ENABLED : NotificationMode.DISABLED;
disposables.add(
subscriptionManager.updateNotificationMode(currentInfo.getServiceId(),
currentInfo.getUrl(), mode)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index ee16a403d..82e923d94 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -36,7 +36,7 @@ class NotificationWorker(
.map { feed ->
feed.mapNotNull { x ->
x.value?.takeIf {
- it.notificationMode == NotificationMode.ENABLED_DEFAULT &&
+ it.notificationMode == NotificationMode.ENABLED &&
it.newStreamsCount > 0
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
index eb94fb843..9c4797237 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
@@ -92,7 +92,7 @@ class NotificationsChannelsConfigFragment : Fragment(), ModeToggleListener {
val subscriptions = adapter?.getCurrentList() ?: return
val mode = subscriptions.firstOrNull()?.notificationMode ?: return
val newMode = when (mode) {
- NotificationMode.DISABLED -> NotificationMode.ENABLED_DEFAULT
+ NotificationMode.DISABLED -> NotificationMode.ENABLED
else -> NotificationMode.DISABLED
}
val subscriptionManager = SubscriptionManager(requireContext())
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
index 1689747e2..2fa916166 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
@@ -85,7 +85,7 @@ class NotificationsConfigAdapter(
val mode = if (checkedTextView.isChecked) {
NotificationMode.DISABLED
} else {
- NotificationMode.ENABLED_DEFAULT
+ NotificationMode.ENABLED
}
listener.onModeToggle(adapterPosition, mode)
}
From 4f7cdcce5568dc8973a42d837e49d9b1d966f2b0 Mon Sep 17 00:00:00 2001
From: Tobi
Date: Fri, 15 Oct 2021 20:22:12 +0200
Subject: [PATCH 011/213] Update
app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
Co-authored-by: litetex <40789489+litetex@users.noreply.github.com>
---
.../newpipe/fragments/list/channel/ChannelFragment.java | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index 8ed6b5149..752b05d5a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -400,17 +400,16 @@ public class ChannelFragment extends BaseListInfoFragment
if (menuNotifyButton == null) {
return;
}
- if (subscription == null) {
- menuNotifyButton.setVisible(false);
- } else {
+ if (subscription != null) {
menuNotifyButton.setEnabled(
NotificationHelper.isNewStreamsNotificationsEnabled(requireContext())
);
menuNotifyButton.setChecked(
- subscription.getNotificationMode() != NotificationMode.DISABLED
+ subscription.getNotificationMode() == NotificationMode.ENABLED
);
- menuNotifyButton.setVisible(true);
}
+
+ menuNotifyButton.setVisible(subscription != null);
}
private void setNotify(final boolean isEnabled) {
From 793ff1a72847e6ecc313d912eb19368dd1f57f2f Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Fri, 15 Oct 2021 20:57:49 +0200
Subject: [PATCH 012/213] Add a few comments and rename a few methods
---
.../fragments/list/channel/ChannelFragment.java | 9 ++++++---
.../feed/notifications/NotificationHelper.kt | 16 +++++++++++++---
.../feed/notifications/NotificationWorker.kt | 6 +++++-
.../local/feed/notifications/ScheduleOptions.kt | 4 ++++
4 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index 752b05d5a..5e2e24fab 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -259,7 +259,7 @@ public class ChannelFragment extends BaseListInfoFragment
.map(List::isEmpty)
.distinctUntilChanged()
.skip(1) // channel has just been opened
- .filter(x -> NotificationHelper.isNewStreamsNotificationsEnabled(requireContext()))
+ .filter(x -> NotificationHelper.areNewStreamsNotificationsEnabled(requireContext()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(isEmpty -> {
if (!isEmpty) {
@@ -402,13 +402,13 @@ public class ChannelFragment extends BaseListInfoFragment
}
if (subscription != null) {
menuNotifyButton.setEnabled(
- NotificationHelper.isNewStreamsNotificationsEnabled(requireContext())
+ NotificationHelper.areNewStreamsNotificationsEnabled(requireContext())
);
menuNotifyButton.setChecked(
subscription.getNotificationMode() == NotificationMode.ENABLED
);
}
-
+
menuNotifyButton.setVisible(subscription != null);
}
@@ -423,6 +423,9 @@ public class ChannelFragment extends BaseListInfoFragment
);
}
+ /**
+ * Show a snackbar with the option to enable notifications on new streams for this channel.
+ */
private void showNotifySnackbar() {
Snackbar.make(itemsList, R.string.you_successfully_subscribed, Snackbar.LENGTH_LONG)
.setAction(R.string.get_notified, v -> setNotify(true))
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
index ec5cb790f..fac3b8f72 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -21,13 +21,20 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.local.feed.service.FeedUpdateInfo
import org.schabi.newpipe.util.NavigationHelper
+/**
+ * Helper for everything related to show notifications about new streams to the user.
+ */
class NotificationHelper(val context: Context) {
private val manager = context.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
- fun notify(data: FeedUpdateInfo): Completable {
+ /**
+ * Show a notification about new streams from a single channel.
+ * Opening the notification will open the corresponding channel page.
+ */
+ fun displayNewStreamsNotification(data: FeedUpdateInfo): Completable {
val newStreams: List = data.newStreams
val summary = context.resources.getQuantityString(
R.plurals.new_streams, newStreams.size, newStreams.size
@@ -69,11 +76,14 @@ class NotificationHelper(val context: Context) {
style.setSummaryText(summary)
style.setBigContentTitle(data.name)
builder.setStyle(style)
+ // open the channel page when clicking on the notification
builder.setContentIntent(
PendingIntent.getActivity(
context,
data.pseudoId,
- NavigationHelper.getChannelIntent(context, data.listInfo.serviceId, data.listInfo.url)
+ NavigationHelper.getChannelIntent(
+ context, data.listInfo.serviceId, data.listInfo.url
+ )
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
0
)
@@ -110,7 +120,7 @@ class NotificationHelper(val context: Context) {
}
@JvmStatic
- fun isNewStreamsNotificationsEnabled(context: Context): Boolean {
+ fun areNewStreamsNotificationsEnabled(context: Context): Boolean {
return (
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.enable_streams_notifications), false) &&
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index 82e923d94..daae52fdd 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -21,6 +21,10 @@ import org.schabi.newpipe.local.feed.service.FeedLoadManager
import org.schabi.newpipe.local.feed.service.FeedLoadService
import java.util.concurrent.TimeUnit
+/*
+ * Worker which checks for new streams of subscribed channels
+ * in intervals which can be set by the user in the settings.
+ */
class NotificationWorker(
appContext: Context,
workerParams: WorkerParameters,
@@ -43,7 +47,7 @@ class NotificationWorker(
}
.doOnSubscribe { setForegroundAsync(createForegroundInfo()) }
.flatMapObservable { Observable.fromIterable(it) }
- .flatMapCompletable { x -> notificationHelper.notify(x) }
+ .flatMapCompletable { x -> notificationHelper.displayNewStreamsNotification(x) }
.toSingleDefault(Result.success())
.onErrorReturnItem(Result.failure())
} else Single.just(Result.success())
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
index 30e8d5515..0dbc15395 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/ScheduleOptions.kt
@@ -5,6 +5,10 @@ import androidx.preference.PreferenceManager
import org.schabi.newpipe.R
import java.util.concurrent.TimeUnit
+/**
+ * Information for the Scheduler which checks for new streams.
+ * See [NotificationWorker]
+ */
data class ScheduleOptions(
val interval: Long,
val isRequireNonMeteredNetwork: Boolean
From 7d4c7718aa64dd00c2a64b9d72df764aad9977fd Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Mon, 18 Oct 2021 13:11:44 +0200
Subject: [PATCH 013/213] comments & rename
---
.../feed/notifications/NotificationIcon.kt | 4 ++++
...pter.kt => NotificationModeConfigAdapter.kt} | 16 ++++++++++++----
...ent.kt => NotificationModeConfigFragment.kt} | 17 ++++++++++++-----
app/src/main/res/xml/notifications_settings.xml | 2 +-
4 files changed, 29 insertions(+), 10 deletions(-)
rename app/src/main/java/org/schabi/newpipe/settings/notifications/{NotificationsConfigAdapter.kt => NotificationModeConfigAdapter.kt} (85%)
rename app/src/main/java/org/schabi/newpipe/settings/notifications/{NotificationsChannelsConfigFragment.kt => NotificationModeConfigFragment.kt} (84%)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
index 1073945d4..0fb6877a6 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationIcon.kt
@@ -10,6 +10,10 @@ import io.reactivex.rxjava3.core.SingleEmitter
import io.reactivex.rxjava3.core.SingleOnSubscribe
import org.schabi.newpipe.util.PicassoHelper
+/**
+ * Helper class to handle loading and resizing of icons
+ * which are used going to be used in notifications.
+ */
internal class NotificationIcon(
context: Context,
private val url: String,
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigAdapter.kt
similarity index 85%
rename from app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
rename to app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigAdapter.kt
index 2fa916166..156877b4e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsConfigAdapter.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigAdapter.kt
@@ -10,9 +10,14 @@ import androidx.recyclerview.widget.RecyclerView
import org.schabi.newpipe.R
import org.schabi.newpipe.database.subscription.NotificationMode
import org.schabi.newpipe.database.subscription.SubscriptionEntity
-import org.schabi.newpipe.settings.notifications.NotificationsConfigAdapter.SubscriptionHolder
+import org.schabi.newpipe.settings.notifications.NotificationModeConfigAdapter.SubscriptionHolder
-class NotificationsConfigAdapter(
+/**
+ * This [RecyclerView.Adapter] is used in the [NotificationModeConfigFragment].
+ * The adapter holds all subscribed channels and their [NotificationMode]s
+ * and provides the needed data structures and methods for this task.
+ */
+class NotificationModeConfigAdapter(
private val listener: ModeToggleListener
) : RecyclerView.Adapter() {
@@ -87,7 +92,7 @@ class NotificationsConfigAdapter(
} else {
NotificationMode.ENABLED
}
- listener.onModeToggle(adapterPosition, mode)
+ listener.onModeChange(adapterPosition, mode)
}
}
@@ -111,6 +116,9 @@ class NotificationsConfigAdapter(
}
interface ModeToggleListener {
- fun onModeToggle(position: Int, @NotificationMode mode: Int)
+ /**
+ * Triggered when the UI representation of a notification mode is changed.
+ */
+ fun onModeChange(position: Int, @NotificationMode mode: Int)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
similarity index 84%
rename from app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
rename to app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
index 9c4797237..9021fd68c 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationsChannelsConfigFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
@@ -16,13 +16,18 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.R
import org.schabi.newpipe.database.subscription.NotificationMode
import org.schabi.newpipe.local.subscription.SubscriptionManager
-import org.schabi.newpipe.settings.notifications.NotificationsConfigAdapter.ModeToggleListener
+import org.schabi.newpipe.settings.notifications.NotificationModeConfigAdapter.ModeToggleListener
-class NotificationsChannelsConfigFragment : Fragment(), ModeToggleListener {
+/**
+ * [NotificationModeConfigFragment] is a settings fragment
+ * which allows changing the [NotificationMode] of all subscribed channels.
+ * The [NotificationMode] can either be changed one by one or toggled for all channels.
+ */
+class NotificationModeConfigFragment : Fragment(), ModeToggleListener {
private lateinit var updaters: CompositeDisposable
private var loader: Disposable? = null
- private var adapter: NotificationsConfigAdapter? = null
+ private var adapter: NotificationModeConfigAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -39,7 +44,7 @@ class NotificationsChannelsConfigFragment : Fragment(), ModeToggleListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view)
- adapter = NotificationsConfigAdapter(this)
+ adapter = NotificationModeConfigAdapter(this)
recyclerView.adapter = adapter
loader?.dispose()
loader = SubscriptionManager(requireContext())
@@ -74,7 +79,9 @@ class NotificationsChannelsConfigFragment : Fragment(), ModeToggleListener {
}
}
- override fun onModeToggle(position: Int, @NotificationMode mode: Int) {
+ override fun onModeChange(position: Int, @NotificationMode mode: Int) {
+ // Notification mode has been changed via the UI.
+ // Now change it in the database.
val subscription = adapter?.getItem(position) ?: return
updaters.add(
SubscriptionManager(requireContext())
diff --git a/app/src/main/res/xml/notifications_settings.xml b/app/src/main/res/xml/notifications_settings.xml
index 4390dc48c..60d0428f7 100644
--- a/app/src/main/res/xml/notifications_settings.xml
+++ b/app/src/main/res/xml/notifications_settings.xml
@@ -32,7 +32,7 @@
app:iconSpaceReserved="false" />
Date: Fri, 22 Oct 2021 21:24:22 +0200
Subject: [PATCH 014/213] Fix check wether the app's notifications are disabled
via system settings
Add comments
Rename a few methods
---
.../feed/notifications/NotificationHelper.kt | 30 +++++++++++++++----
.../feed/notifications/NotificationWorker.kt | 2 +-
.../settings/NotificationsSettingsFragment.kt | 6 ++--
.../schabi/newpipe/util/NavigationHelper.java | 8 ++---
4 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
index fac3b8f72..fa26be37a 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -101,39 +101,57 @@ class NotificationHelper(val context: Context) {
companion object {
/**
- * Check whether notifications are not disabled by user via system settings.
+ * Check whether notifications are enabled on the device.
+ * Users can disable them via the system settings for a single app.
+ * If this is the case, the app cannot create any notifications
+ * and display them to the user.
+ *
+ * On Android 26 and above, notification channels are used by NewPipe.
+ * These can be configured by the user, too.
+ * The notification channel for new streams is also checked by this method.
*
* @param context Context
- * @return true if notifications are allowed, false otherwise
+ * @return true
if notifications are allowed and can be displayed;
+ * false
otherwise
*/
- fun isNotificationsEnabledNative(context: Context): Boolean {
+ fun areNotificationsEnabledOnDevice(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = context.getString(R.string.streams_notification_channel_id)
val manager = context.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
+ val enabled = manager.areNotificationsEnabled()
val channel = manager.getNotificationChannel(channelId)
- channel != null && channel.importance != NotificationManager.IMPORTANCE_NONE
+ val importance = channel?.importance
+ enabled && channel != null && importance != NotificationManager.IMPORTANCE_NONE
} else {
NotificationManagerCompat.from(context).areNotificationsEnabled()
}
}
@JvmStatic
+ /**
+ * Whether the user enabled the notifications for new streams in the app settings.
+ */
fun areNewStreamsNotificationsEnabled(context: Context): Boolean {
return (
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.enable_streams_notifications), false) &&
- isNotificationsEnabledNative(context)
+ areNotificationsEnabledOnDevice(context)
)
}
- fun openNativeSettingsScreen(context: Context) {
+ /**
+ * Open the system's notification settings for NewPipe on Android O (API 26) and later.
+ * Open the system's app settings for NewPipe on previous Android versions.
+ */
+ fun openNewPipeSystemNotificationSettings(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = context.getString(R.string.streams_notification_channel_id)
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
.putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
} else {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index daae52fdd..afdeee7f4 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -75,7 +75,7 @@ class NotificationWorker(
.getBoolean(
context.getString(R.string.enable_streams_notifications),
false
- ) && NotificationHelper.isNotificationsEnabledNative(context)
+ ) && NotificationHelper.areNotificationsEnabledOnDevice(context)
}
fun schedule(context: Context, options: ScheduleOptions, force: Boolean = false) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
index 50fb95450..938b7ff8d 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
@@ -47,7 +47,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
override fun onResume() {
super.onResume()
- val enabled = NotificationHelper.isNotificationsEnabledNative(requireContext())
+ val enabled = NotificationHelper.areNotificationsEnabledOnDevice(requireContext())
preferenceScreen.isEnabled = enabled
if (!enabled) {
if (notificationWarningSnackbar == null) {
@@ -56,8 +56,8 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
R.string.notifications_disabled,
Snackbar.LENGTH_INDEFINITE
).apply {
- setAction(R.string.settings) { v ->
- NotificationHelper.openNativeSettingsScreen(v.context)
+ setAction(R.string.settings) {
+ activity?.let { NotificationHelper.openNewPipeSystemNotificationSettings(it) }
}
setActionTextColor(Color.YELLOW)
addCallback(object : Snackbar.Callback() {
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 80267a9dd..f70002409 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -1,5 +1,7 @@
package org.schabi.newpipe.util;
+import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
@@ -18,6 +20,8 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import com.jakewharton.processphoenix.ProcessPhoenix;
+
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
@@ -57,10 +61,6 @@ import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.ArrayList;
-import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp;
-
-import com.jakewharton.processphoenix.ProcessPhoenix;
-
public final class NavigationHelper {
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
From 77aaa15082f82b6f4d2d102b9ee873b9cb7ddb97 Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Mon, 25 Oct 2021 13:59:49 +0200
Subject: [PATCH 015/213] Fix toggling the system's settings for app
notification
Do not open the setting for a specific notification channel (Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS), but the settings for all notifications by the app (Settings.ACTION_APP_NOTIFICATION_SETTINGS)
---
.../newpipe/local/feed/notifications/NotificationHelper.kt | 6 ++----
.../newpipe/settings/NotificationsSettingsFragment.kt | 2 +-
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
index fa26be37a..87101abcd 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -142,15 +142,13 @@ class NotificationHelper(val context: Context) {
}
/**
- * Open the system's notification settings for NewPipe on Android O (API 26) and later.
+ * Open the system's notification settings for NewPipe on Android Oreo (API 26) and later.
* Open the system's app settings for NewPipe on previous Android versions.
*/
fun openNewPipeSystemNotificationSettings(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val channelId = context.getString(R.string.streams_notification_channel_id)
- val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
- .putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
} else {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
index 938b7ff8d..e1f7ed2c2 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
@@ -57,7 +57,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
Snackbar.LENGTH_INDEFINITE
).apply {
setAction(R.string.settings) {
- activity?.let { NotificationHelper.openNewPipeSystemNotificationSettings(it) }
+ NotificationHelper.openNewPipeSystemNotificationSettings(it.context)
}
setActionTextColor(Color.YELLOW)
addCallback(object : Snackbar.Callback() {
From 2d2b96420f867b6481908247e5c6499cf1b4762f Mon Sep 17 00:00:00 2001
From: TobiGr
Date: Mon, 25 Oct 2021 15:06:15 +0200
Subject: [PATCH 016/213] Add comments and improve code formatting
---
.../list/channel/ChannelFragment.java | 8 +--
.../feed/notifications/NotificationHelper.kt | 7 ++-
.../local/feed/service/FeedLoadManager.kt | 53 ++++++++++++++++---
3 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
index 5e2e24fab..fe0122ea5 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java
@@ -413,10 +413,12 @@ public class ChannelFragment extends BaseListInfoFragment
}
private void setNotify(final boolean isEnabled) {
- final int mode = isEnabled ? NotificationMode.ENABLED : NotificationMode.DISABLED;
disposables.add(
- subscriptionManager.updateNotificationMode(currentInfo.getServiceId(),
- currentInfo.getUrl(), mode)
+ subscriptionManager
+ .updateNotificationMode(
+ currentInfo.getServiceId(),
+ currentInfo.getUrl(),
+ isEnabled ? NotificationMode.ENABLED : NotificationMode.DISABLED)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
index 87101abcd..a9cdd852d 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -81,9 +81,8 @@ class NotificationHelper(val context: Context) {
PendingIntent.getActivity(
context,
data.pseudoId,
- NavigationHelper.getChannelIntent(
- context, data.listInfo.serviceId, data.listInfo.url
- )
+ NavigationHelper
+ .getChannelIntent(context, data.listInfo.serviceId, data.listInfo.url)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
0
)
@@ -129,10 +128,10 @@ class NotificationHelper(val context: Context) {
}
}
- @JvmStatic
/**
* Whether the user enabled the notifications for new streams in the app settings.
*/
+ @JvmStatic
fun areNewStreamsNotificationsEnabled(context: Context): Boolean {
return (
PreferenceManager.getDefaultSharedPreferences(context)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
index 6a44935c5..528bcc5d2 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt
@@ -37,6 +37,15 @@ class FeedLoadManager(private val context: Context) {
FeedLoadState(description, maxProgress.get(), currentProgress.get())
}
+ /**
+ * Start checking for new streams of a subscription group.
+ * @param groupId The ID of the subscription group to load.
+ * When using [FeedGroupEntity.GROUP_ALL_ID], all subscriptions are loaded.
+ * @param ignoreOutdatedThreshold When `false`, only subscriptions which have not been updated
+ * within the `feed_update_threshold` are checked for updates.
+ * This threshold can be set by the user in the app settings.
+ * When `true`, all subscriptions are checked for new streams.
+ */
fun startLoading(
groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
ignoreOutdatedThreshold: Boolean = false,
@@ -59,12 +68,15 @@ class FeedLoadManager(private val context: Context) {
OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong())
}
- val subscriptions = when (groupId) {
+ /**
+ * subscriptions which have not been updated within the feed updated threshold
+ */
+ val outdatedSubscriptions = when (groupId) {
FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold)
else -> feedDatabaseManager.outdatedSubscriptionsForGroup(groupId, outdatedThreshold)
}
- return subscriptions
+ return outdatedSubscriptions
.take(1)
.doOnNext {
@@ -90,9 +102,14 @@ class FeedLoadManager(private val context: Context) {
.map { subscriptionEntity ->
var error: Throwable? = null
try {
+ // check for and load new streams
+ // either by using the dedicated feed method or by getting the channel info
val listInfo = if (useFeedExtractor) {
ExtractorHelper
- .getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
+ .getFeedInfoFallbackToChannelInfo(
+ subscriptionEntity.serviceId,
+ subscriptionEntity.url
+ )
.onErrorReturn {
error = it // store error, otherwise wrapped into RuntimeException
throw it
@@ -100,7 +117,11 @@ class FeedLoadManager(private val context: Context) {
.blockingGet()
} else {
ExtractorHelper
- .getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
+ .getChannelInfo(
+ subscriptionEntity.serviceId,
+ subscriptionEntity.url,
+ true
+ )
.onErrorReturn {
error = it // store error, otherwise wrapped into RuntimeException
throw it
@@ -108,7 +129,12 @@ class FeedLoadManager(private val context: Context) {
.blockingGet()
} as ListInfo
- return@map Notification.createOnNext(FeedUpdateInfo(subscriptionEntity, listInfo))
+ return@map Notification.createOnNext(
+ FeedUpdateInfo(
+ subscriptionEntity,
+ listInfo
+ )
+ )
} catch (e: Throwable) {
if (error == null) {
// do this to prevent blockingGet() from wrapping into RuntimeException
@@ -116,7 +142,8 @@ class FeedLoadManager(private val context: Context) {
}
val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
- val wrapper = FeedLoadService.RequestException(subscriptionEntity.uid, request, error!!)
+ val wrapper =
+ FeedLoadService.RequestException(subscriptionEntity.uid, request, error!!)
return@map Notification.createOnError(wrapper)
}
}
@@ -142,6 +169,13 @@ class FeedLoadManager(private val context: Context) {
FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(currentProgress.get(), maxProgress.get()))
}
+ /**
+ * Keep the feed and the stream tables small
+ * to reduce loading times when trying to display the feed.
+ *
+ * Remove streams from the feed which are older than [FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE].
+ * Remove streams from the database which are not linked / used by any table.
+ */
private fun postProcessFeed() = Completable.fromRunnable {
FeedEventManager.postEvent(FeedEventManager.Event.ProgressEvent(R.string.feed_processing_message))
feedDatabaseManager.removeOrphansOrOlderStreams()
@@ -179,7 +213,12 @@ class FeedLoadManager(private val context: Context) {
subscriptionManager.updateFromInfo(subscriptionId, info)
if (info.errors.isNotEmpty()) {
- feedResultsHolder.addErrors(FeedLoadService.RequestException.wrapList(subscriptionId, info))
+ feedResultsHolder.addErrors(
+ FeedLoadService.RequestException.wrapList(
+ subscriptionId,
+ info
+ )
+ )
feedDatabaseManager.markAsOutdated(subscriptionId)
}
}
From 34ab93c9bd214c02882386db09392e02b59b0540 Mon Sep 17 00:00:00 2001
From: ktprograms
Date: Mon, 1 Nov 2021 11:48:20 +0800
Subject: [PATCH 017/213] Fix player controls not hiding if resumed from media
button
---
app/src/main/java/org/schabi/newpipe/player/Player.java | 3 +++
.../org/schabi/newpipe/player/playback/PlayerMediaSession.java | 1 +
2 files changed, 4 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index 22e66e793..4e2a9d065 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -3754,6 +3754,9 @@ public final class Player implements
case KeyEvent.KEYCODE_SPACE:
if (isFullscreen) {
playPause();
+ if (isPlaying()) {
+ hideControls(0, 0);
+ }
}
break;
case KeyEvent.KEYCODE_BACK:
diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java
index 9dcb12344..fbe12efb1 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java
@@ -88,6 +88,7 @@ public class PlayerMediaSession implements MediaSessionCallback {
@Override
public void play() {
player.play();
+ player.hideControls(0, 0);
}
@Override
From 707f2835a8f9cd2ffb387c81d87e50399309eb1a Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 22:26:05 +0100
Subject: [PATCH 018/213] Restructured build.gradle/androidxWorkVersion
---
app/build.gradle | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 488bfa3ba..b8daca97e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -103,6 +103,7 @@ ext {
androidxLifecycleVersion = '2.3.1'
androidxRoomVersion = '2.3.0'
+ androidxWorkVersion = '2.5.0'
icepickVersion = '3.2.0'
exoPlayerVersion = '2.12.3'
@@ -113,7 +114,6 @@ ext {
leakCanaryVersion = '2.5'
stethoVersion = '1.6.0'
mockitoVersion = '3.6.0'
- workVersion = '2.5.0'
}
configurations {
@@ -222,8 +222,8 @@ dependencies {
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.webkit:webkit:1.4.0'
implementation 'com.google.android.material:material:1.2.1'
- implementation "androidx.work:work-runtime-ktx:${workVersion}"
- implementation "androidx.work:work-rxjava3:${workVersion}"
+ implementation "androidx.work:work-runtime-ktx:${androidxWorkVersion}"
+ implementation "androidx.work:work-rxjava3:${androidxWorkVersion}"
/** Third-party libraries **/
// Instance state boilerplate elimination
From 4f8552835ea94e21e1e7d65d41b253379920c2d1 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 22:43:23 +0100
Subject: [PATCH 019/213] Better naming for a test class that does database
migrations
---
.../database/{AppDatabaseTest.kt => DatabaseMigrationTest.kt} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename app/src/androidTest/java/org/schabi/newpipe/database/{AppDatabaseTest.kt => DatabaseMigrationTest.kt} (99%)
diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt
similarity index 99%
rename from app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
rename to app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt
index 158994cdd..28dea13e9 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
+++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt
@@ -16,7 +16,7 @@ import org.junit.runner.RunWith
import org.schabi.newpipe.extractor.stream.StreamType
@RunWith(AndroidJUnit4::class)
-class AppDatabaseTest {
+class DatabaseMigrationTest {
companion object {
private const val DEFAULT_SERVICE_ID = 0
private const val DEFAULT_URL = "https://www.youtube.com/watch?v=cDphUib5iG4"
From e4cd52060cde3dfb551a6a5efdd83a325659c220 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 22:48:49 +0100
Subject: [PATCH 020/213] Reformatted code so that it's better readable
---
app/src/main/java/org/schabi/newpipe/App.java | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 3601e5c61..0cfa4ee62 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -226,29 +226,33 @@ public class App extends MultiDexApplication {
// Keep the importance below DEFAULT to avoid making noise on every notification update for
// the main and update channels
final NotificationChannelCompat mainChannel = new NotificationChannelCompat
- .Builder(getString(R.string.notification_channel_id),
- NotificationManagerCompat.IMPORTANCE_LOW)
+ .Builder(
+ getString(R.string.notification_channel_id),
+ NotificationManagerCompat.IMPORTANCE_LOW)
.setName(getString(R.string.notification_channel_name))
.setDescription(getString(R.string.notification_channel_description))
.build();
final NotificationChannelCompat appUpdateChannel = new NotificationChannelCompat
- .Builder(getString(R.string.app_update_notification_channel_id),
- NotificationManagerCompat.IMPORTANCE_LOW)
+ .Builder(
+ getString(R.string.app_update_notification_channel_id),
+ NotificationManagerCompat.IMPORTANCE_LOW)
.setName(getString(R.string.app_update_notification_channel_name))
.setDescription(getString(R.string.app_update_notification_channel_description))
.build();
final NotificationChannelCompat hashChannel = new NotificationChannelCompat
- .Builder(getString(R.string.hash_channel_id),
- NotificationManagerCompat.IMPORTANCE_HIGH)
+ .Builder(
+ getString(R.string.hash_channel_id),
+ NotificationManagerCompat.IMPORTANCE_HIGH)
.setName(getString(R.string.hash_channel_name))
.setDescription(getString(R.string.hash_channel_description))
.build();
final NotificationChannelCompat newStreamsChannel = new NotificationChannelCompat
- .Builder(getString(R.string.streams_notification_channel_id),
- NotificationManagerCompat.IMPORTANCE_DEFAULT)
+ .Builder(
+ getString(R.string.streams_notification_channel_id),
+ NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setName(getString(R.string.streams_notification_channel_name))
.setDescription(getString(R.string.streams_notification_channel_description))
.build();
From 58418bcf460390d9a195e15254cfaa17a9685b51 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 22:57:31 +0100
Subject: [PATCH 021/213] Improved code readability
---
.../newpipe/local/feed/notifications/NotificationWorker.kt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
index afdeee7f4..6886c1e03 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt
@@ -87,6 +87,7 @@ class NotificationWorker(
NetworkType.CONNECTED
}
).build()
+
val request = PeriodicWorkRequest.Builder(
NotificationWorker::class.java,
options.interval,
@@ -95,6 +96,7 @@ class NotificationWorker(
.addTag(TAG)
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES)
.build()
+
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
TAG,
From 0f4b6d7d9f1af7a12b16aadedfc2c8833b11c906 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 23:22:52 +0100
Subject: [PATCH 022/213] Improved code readablity
---
.../schabi/newpipe/settings/NotificationsSettingsFragment.kt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
index e1f7ed2c2..04f5a9b56 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationsSettingsFragment.kt
@@ -47,6 +47,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
override fun onResume() {
super.onResume()
+
val enabled = NotificationHelper.areNotificationsEnabledOnDevice(requireContext())
preferenceScreen.isEnabled = enabled
if (!enabled) {
@@ -73,6 +74,8 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
notificationWarningSnackbar?.dismiss()
notificationWarningSnackbar = null
}
+
+ // (Re-)Create loader
loader?.dispose()
loader = SubscriptionManager(requireContext())
.subscriptions()
@@ -83,6 +86,7 @@ class NotificationsSettingsFragment : BasePreferenceFragment(), OnSharedPreferen
override fun onPause() {
loader?.dispose()
loader = null
+
super.onPause()
}
From 94219b78e71be292e215b7cd41dd3976122cb330 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 23:22:59 +0100
Subject: [PATCH 023/213] Fixed typos
---
app/src/main/java/org/schabi/newpipe/MainActivity.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index dda0dd0e4..2e3eec6c1 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -165,7 +165,7 @@ public class MainActivity extends AppCompatActivity {
}
openMiniPlayerUponPlayerStarted();
- // shedule worker for checking for new streans and creating corresponding notifications
+ // schedule worker for checking for new streams and creating corresponding notifications
NotificationWorker.schedule(this);
}
From f0112a2de2348c9532755dc132c2d4e505463d86 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 23:36:46 +0100
Subject: [PATCH 024/213] Added some lines to improve code-readability
---
.../newpipe/local/feed/notifications/NotificationHelper.kt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
index a9cdd852d..2196da0d7 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt
@@ -69,6 +69,8 @@ class NotificationHelper(val context: Context) {
.setColorized(true)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
+
+ // Build style
val style = NotificationCompat.InboxStyle()
for (stream in newStreams) {
style.addLine(stream.name)
@@ -76,6 +78,7 @@ class NotificationHelper(val context: Context) {
style.setSummaryText(summary)
style.setBigContentTitle(data.name)
builder.setStyle(style)
+
// open the channel page when clicking on the notification
builder.setContentIntent(
PendingIntent.getActivity(
@@ -87,6 +90,7 @@ class NotificationHelper(val context: Context) {
0
)
)
+
return Single.create(NotificationIcon(context, data.avatarUrl))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
From bc68836c8d167dc4a57f79018cd110391b3b43bc Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 2 Nov 2021 23:59:48 +0100
Subject: [PATCH 025/213] Reworked menu_channel.xml
---
app/src/main/res/menu/menu_channel.xml | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/src/main/res/menu/menu_channel.xml b/app/src/main/res/menu/menu_channel.xml
index d6c54b680..5ea8f8c95 100644
--- a/app/src/main/res/menu/menu_channel.xml
+++ b/app/src/main/res/menu/menu_channel.xml
@@ -20,19 +20,21 @@
+ android:visible="false"
+ app:showAsAction="never"
+ tools:visible="true" />
From 5ae72d1ed216026bb21b001456dcde72ad68a352 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 3 Nov 2021 00:11:44 +0100
Subject: [PATCH 026/213] Removed unknown/unused file
---
assets/db.dia | Bin 3167 -> 0 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 assets/db.dia
diff --git a/assets/db.dia b/assets/db.dia
deleted file mode 100644
index 1194f488299483bd0fb0fad7ce650a09b4b5bbc5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3167
zcmV-l450HLiwFP!000021MOYQlA|~l-Pcz*Tw5oJc+>8zj+yD0=v_z5)T{~FC~dkh
zc!Zo;-psd;u$_KCFapX@CnKsdZOgiFIf!$QuC9Ll?fWe5y)m9eY4Xx1n%?iRWSoXk
zGI{C$=j%V8y#8-rKK&X-^cVSW!s)Chzma75tC#*%i1{zi&+qT=TD;7tNVyhA3oT>M
z|D$nCpJk)x{+Ca^-hKxm6*PZu^DY&FN27&cy@bx#OMgVi-zGd=Bw>HuYSV6<#wqW;
z(fFnR`E~Wvf8KQSe9+I8zUOqpMx4=am%G;GkMEc1dd~Q9x3hVgMY4%l&QF?L=qLYw
zug#`aCL1M_FQ5NreO@2Z=8aun=MMCu5iz5D5+$d0q;KN2BMi+m3`07@_B~&BEth=w
zX`LgM-S_j
ziI^@K-*o?nymX9Dc>bL3ZLaWU$m!9sh~7S4?KveK{l&&&bGEOqVT2vr+(`q$)WJXVe@p0Dsw>GV+WiLAD8+bjgGtK^6+UgiP^6EA<;F@
zvIoT1N}8H6LHjo_9!6tPu6Gu*Sujp{a!Nfaan!2+)$nxctdF6|l^(MEy&q4()|5q)
zskm&+&|LlC$0%n^u1vHZ>GEXV{E>?}>b$B?`OXDktyY$^Bu@ld>A=Nb{Z3&nIIx#^^CGx90G
z_zDPf9!WmD48F1DRbz+b^d4KIG!FO8WObcXl1geH2e0X=-}3i*uXGm0OF4k2Nt(^+
zSU$osvCJQX@&UH);ve$9ix@AD_1o4nnq*tU-q!LTEPi7m8q=r0OMjI+fDLHwJ%-am
z_;VCT|6QnGhMR-Gd=?*#GnS(73z&5L!yiZ8+s#?szRqrO5RtRAVB7wb%-@tU&X3no
zctO*+5(r!x?c{-8vlyqzq%vctLu#?7L>XJ>N7Z6s|NYjIMI5gv`7Ki?5&HeM7RhR4
zsM6BeK&Lra&v`oGEXx8B%^09vCA2HRTQ{+NUWjFi!I8lwl?SWy|7=`EE4-H-O
z&4FpQIS4QBry@Z(v89J&Jn&QCHxdA?8NRjhGuTH)8I`jky1%e27v(B?tw>g|r#Zx~AU(8Dim+MsY9J@LZEJDoF_l
zPB3zUo4|AaImA;;d|_G_=mxqUR#h^qQk96;fT~2iovLKDy40JZ*B(w)a*ipu36bgN
z5ScierGf>}nxHj7Yl7AUtqEEav?gdxU0;75A|8j@1PK+O2B=X}0hq?L0Hy_iZY>%aJ+*`Q_ib{NHV?eD3FiQ
zy6(}sPLau06`9-tAvMZNdG|wN=c(@l`O9ptu2kMp54SkL(#*rq!iB)}lk5i1C?8LQ
zsf<-pzJ#;{X$jI2q$Nm8k1H)9_C}1-rO4|un>)7VNFl$tj
z_N)w_^87O09GI=JAlIlexloxNLS@pAWhSGB+`Gjn8}n$M&wgi+
znIJPkW`fKFnF(ciAuwSzC(@G;gF{|I2~%Jim`1H>fV!I#TyIoRj-tGPcK1@G*W%uY
zaZGao-kpPY$hOiohHhZHTU0w0(Q7nbuS%SyA%i*sc(*%4!Bfsqa5T#>?3xo4wnm1A
zM*RFQyUm*jt2*uB>I4~@Tgj|Ak`-E)tnkWMh#UpvC?H1xISR;8z)~1wDj-v#aW!?N
zHv!wgcH67;`synN+;swfo}KOzr`BZyho**4tBm;k4NJ31+kTAo166pjXmN@I3gx4m
zFT(VC!t`Lm)5RRYI)Zfs>j>5ntYbj|LK%eVNK(aoInqJO6kH!St2rSF1fZ^8P))fA
zQZ8c_;2OAYcXAWaE2+Oi9A`lCj2)fSU4&)N)bPpIbju4z!CqU^*XVydq>
zuCZT{#X`;##MC;))ZkzOGT$u}@+#z2$g7Z7A+J8Jyt+4jLuiH2ihL+w8`%D#A^gAC
zE8{E~pAF-Qau~lUYHFC8W!D+Qf7mf38?fx*Zk3e|C5zdH)tnr}?lW@bGhN}B=t88Dg9uhrxY7+_q0!BMR>oG*#63{iD+yed
zfaelgz-bsQvMZ-qQq4-jfO4L`Up^;8wd){e$>jPavQ@OKP}%;
zk(@zzKzKlSKzKlSwuFaVBrSIn9;Z>lW2zD!NAq*#fcRRSyD;xr8G4%UDO7cN1kQ8T
zX&0LrL^H3lnGcT?ZD-aU{6Y*o5r7@F&w?!a(rp4HMdf0NNNtia|Yu$~L2ai$^BJcsyqG`u;z
zweu#jdBAovfA_U#N!A}_Dke)ex*N*k(s5ZOH-Kp;RMKp;RMK$b!>v+*O7nO*g|ET-mps+Fb-)@3;~HO${I{rnA;
zawZS4uIr)~ueG%`%d-9I+bUJq!HgJ45l9hIo{{nlTI8$Di5=w+8IAHHuL@q|c&=CT
zcAVR1_PR9PiV_iD!R({3p%dR-YeqEP(Mch4b5C2PdVe57AVMHQAVMHQAVSBB-3=8Y
z$3{HSFBX8>r_!iuu!R9
zThl8kS%^}-kH}-LR_cwCD4Vh{m_=DW&SWUhi}JiE&x`WBD9`)2<$32+OtD)OHX!Lz
zVcrFg0ck+GOOPHd<-;Ke)AuA!X~Fmp={{v
Date: Wed, 3 Nov 2021 08:26:13 +0800
Subject: [PATCH 027/213] Enable play/pause with space key even when not in
fullscreen player
---
app/src/main/java/org/schabi/newpipe/player/Player.java | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index 4e2a9d065..2a88445bb 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -3752,11 +3752,9 @@ public final class Player implements
default:
break;
case KeyEvent.KEYCODE_SPACE:
- if (isFullscreen) {
- playPause();
- if (isPlaying()) {
- hideControls(0, 0);
- }
+ playPause();
+ if (isPlaying()) {
+ hideControls(0, 0);
}
break;
case KeyEvent.KEYCODE_BACK:
From 1289b1a283dbde7bbea8d325cd8873e10c73ade6 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 5 Nov 2021 13:17:33 +0100
Subject: [PATCH 028/213] Code cleanup
---
.../schabi/newpipe/local/subscription/SubscriptionManager.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
index 2911e8d19..bcd64791e 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt
@@ -69,8 +69,8 @@ class SubscriptionManager(context: Context) {
}
}
- fun updateNotificationMode(serviceId: Int, url: String?, @NotificationMode mode: Int): Completable {
- return subscriptionTable().getSubscription(serviceId, url!!)
+ fun updateNotificationMode(serviceId: Int, url: String, @NotificationMode mode: Int): Completable {
+ return subscriptionTable().getSubscription(serviceId, url)
.flatMapCompletable { entity: SubscriptionEntity ->
Completable.fromAction {
entity.notificationMode = mode
From 7b4e5dd107ec44ebde73127a8db690aa913aee62 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 5 Nov 2021 14:10:53 +0100
Subject: [PATCH 029/213] Reworked player-notfication
* Fixed ``release`` ``main_settings.xml``
* Renamed "Notification" to "Player-Notification" (also reset all translations)
---
.../newpipe/settings/NewPipeSettings.java | 2 +-
... => PlayerNotificationSettingsFragment.kt} | 4 +--
app/src/main/res/values-ar/strings.xml | 1 -
app/src/main/res/values-az/strings.xml | 1 -
app/src/main/res/values-b+ast/strings.xml | 1 -
app/src/main/res/values-b+uz+Latn/strings.xml | 1 -
.../main/res/values-b+zh+HANS+CN/strings.xml | 1 -
app/src/main/res/values-be/strings.xml | 1 -
app/src/main/res/values-bn-rIN/strings.xml | 1 -
app/src/main/res/values-bn/strings.xml | 1 -
app/src/main/res/values-ca/strings.xml | 1 -
app/src/main/res/values-ckb/strings.xml | 1 -
app/src/main/res/values-cs/strings.xml | 1 -
app/src/main/res/values-de/strings.xml | 3 +-
app/src/main/res/values-el/strings.xml | 1 -
app/src/main/res/values-eo/strings.xml | 1 -
app/src/main/res/values-es/strings.xml | 1 -
app/src/main/res/values-et/strings.xml | 1 -
app/src/main/res/values-eu/strings.xml | 1 -
app/src/main/res/values-fa/strings.xml | 1 -
app/src/main/res/values-fi/strings.xml | 1 -
app/src/main/res/values-fr/strings.xml | 1 -
app/src/main/res/values-gl/strings.xml | 1 -
app/src/main/res/values-he/strings.xml | 1 -
app/src/main/res/values-hi/strings.xml | 1 -
app/src/main/res/values-hr/strings.xml | 1 -
app/src/main/res/values-hu/strings.xml | 1 -
app/src/main/res/values-hy/strings.xml | 1 -
app/src/main/res/values-in/strings.xml | 1 -
app/src/main/res/values-it/strings.xml | 1 -
app/src/main/res/values-ja/strings.xml | 1 -
app/src/main/res/values-kmr/strings.xml | 1 -
app/src/main/res/values-ko/strings.xml | 1 -
app/src/main/res/values-ku/strings.xml | 1 -
app/src/main/res/values-lt/strings.xml | 1 -
app/src/main/res/values-lv/strings.xml | 1 -
app/src/main/res/values-ml/strings.xml | 1 -
app/src/main/res/values-ms/strings.xml | 1 -
app/src/main/res/values-nb-rNO/strings.xml | 1 -
app/src/main/res/values-nl-rBE/strings.xml | 1 -
app/src/main/res/values-nl/strings.xml | 1 -
app/src/main/res/values-pa/strings.xml | 1 -
app/src/main/res/values-pl/strings.xml | 1 -
app/src/main/res/values-pt-rBR/strings.xml | 1 -
app/src/main/res/values-pt-rPT/strings.xml | 1 -
app/src/main/res/values-pt/strings.xml | 1 -
app/src/main/res/values-ro/strings.xml | 1 -
app/src/main/res/values-ru/strings.xml | 3 +-
app/src/main/res/values-sc/strings.xml | 1 -
app/src/main/res/values-sk/strings.xml | 1 -
app/src/main/res/values-sl/strings.xml | 1 -
app/src/main/res/values-so/strings.xml | 1 -
app/src/main/res/values-sq/strings.xml | 1 -
app/src/main/res/values-sr/strings.xml | 1 -
app/src/main/res/values-sv/strings.xml | 1 -
app/src/main/res/values-tr/strings.xml | 1 -
app/src/main/res/values-tzm/strings.xml | 1 -
app/src/main/res/values-uk/strings.xml | 3 +-
app/src/main/res/values-ur/strings.xml | 1 -
app/src/main/res/values-vi/strings.xml | 1 -
app/src/main/res/values-zh-rTW/strings.xml | 1 -
app/src/main/res/values/strings.xml | 4 +--
app/src/main/res/xml/appearance_settings.xml | 32 +++++++++----------
...s.xml => player_notification_settings.xml} | 2 +-
app/src/main/res/xml/video_audio_settings.xml | 1 +
app/src/release/res/xml/main_settings.xml | 6 ----
66 files changed, 27 insertions(+), 89 deletions(-)
rename app/src/main/java/org/schabi/newpipe/settings/{NotificationSettingsFragment.kt => PlayerNotificationSettingsFragment.kt} (79%)
rename app/src/main/res/xml/{notification_settings.xml => player_notification_settings.xml} (95%)
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
index aa21c4422..1e1d08856 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
@@ -70,7 +70,7 @@ public final class NewPipeSettings {
PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.history_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.content_settings, true);
- PreferenceManager.setDefaultValues(context, R.xml.notification_settings, true);
+ PreferenceManager.setDefaultValues(context, R.xml.player_notification_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.update_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/PlayerNotificationSettingsFragment.kt
similarity index 79%
rename from app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt
rename to app/src/main/java/org/schabi/newpipe/settings/PlayerNotificationSettingsFragment.kt
index e03aa4074..1789606d1 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/PlayerNotificationSettingsFragment.kt
@@ -5,9 +5,9 @@ import android.os.Bundle
import androidx.preference.Preference
import org.schabi.newpipe.R
-class NotificationSettingsFragment : BasePreferenceFragment() {
+class PlayerNotificationSettingsFragment : BasePreferenceFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.notification_settings)
+ addPreferencesFromResource(R.xml.player_notification_settings)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
val colorizePref: Preference? = findPreference(getString(R.string.notification_colorize_key))
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 55f41563c..0bcb5afbb 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -586,7 +586,6 @@
فقط على شبكة Wi-Fi
بدء التشغيل تلقائياً — %s
تشغيل قائمة الانتظار
- الإشعار
تعذر التعرف على الرابط. فتح باستخدام تطبيق آخر؟
قائمة انتظار تلقائيّة
سيتم استبدال قائمة انتظار للمشغل النشط
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index a5775792a..b7cf7481e 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -104,7 +104,6 @@
Məzmun
Ani pəncərədə oxudulur
Fonda oxudulur
- Bildiriş
Yeniləmələr
Sazlama
Görünüş
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 19ed896f0..513dd5170 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -517,7 +517,6 @@
YouTube forne\'l «Mou torgáu» qu\'anubre conteníu\'l que seya potencialmente p\'adultos
Activar el «Mou torgáu» de YouTube
Amuesa\'l conteníu que quiciabes nun seya afayadizu pa guaḥes porque tien una llende d\'edá (como +18)
- Avisu permanente
Depuración
Namás se sofiten URLs HTTPS
Introduz la URL d\'una instancia
diff --git a/app/src/main/res/values-b+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml
index 4d4602daa..59530ee86 100644
--- a/app/src/main/res/values-b+uz+Latn/strings.xml
+++ b/app/src/main/res/values-b+uz+Latn/strings.xml
@@ -137,7 +137,6 @@
Tarkib
Pop-up rejimda ijro etish
Ijro etish foni
- Bildirishnoma
Yangilanishlar
Nosozliklarni tuzatish
Tashqi ko\'rinish
diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
index 20b01f605..748855731 100644
--- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml
+++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
@@ -554,7 +554,6 @@
第一操作按钮
将通知中视频缩略图的长宽比从 16:9 强制缩放到 1:1(可能会导致失真)
强制缩放缩略图至 1:1 比例
- 通知
显示内存泄漏
已加入播放队列
加入播放队列
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index 4b185b301..5dd5daca1 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -483,7 +483,6 @@
Ператасаваць
Паўтор
Кнопка пятага дзеяння
- Паведамленні
Афарбоўваць апавяшчэнне асноўным колерам мініяцюры. Падтрымваецца не ўсімі прыладамі
У кампактным апавяшчэнні дасяжна не больш за тры дзеянні!
Дзеянні можна змяніць, націснуўшы на іх. Адзначце не больш за трох для адлюстравання ў кампактным апавяшчэнні
diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml
index 831364013..94fa9ee9e 100644
--- a/app/src/main/res/values-bn-rIN/strings.xml
+++ b/app/src/main/res/values-bn-rIN/strings.xml
@@ -291,7 +291,6 @@
বিবরণ
মন্তব্য
- নোটিফিকেশন
মেটা ইনফো দেখান
বিবরণ দেখান
রাত্রি থিম
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 11225e706..ea587e6af 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -332,7 +332,6 @@
- %s সদস্যতাগণ
ব্যবহারকারীরা
- বিজ্ঞপ্তি
বাধার পর প্লে চালিয়ে যাও (উদাহরণস্বরূপ ফোনকল)
সদস্যতা রপ্তানি করা যায়নি
সদস্যতা আমদানি করা যায়নি
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 5e1d1b201..d191d6dcc 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -558,7 +558,6 @@
Notificació de comprovació del vídeo
YouTube proporciona un \"mode restringit\" que amaga contingut potencialment inadequat per a infants
Mostra contingut que podria ser inadequat pels infants
- Notificació
No s\'ha pogut reconèixer l\'adreça URL. Obrir-la amb una altra aplicació\?
Cua automàtica
Desactiveu-ho per deixar de mostrar les metadades, que contenen informació addicional sobre el creador del directe, el contingut o una sol·licitud de cerca
diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml
index 65af5ffff..cc5d7d0fc 100644
--- a/app/src/main/res/values-ckb/strings.xml
+++ b/app/src/main/res/values-ckb/strings.xml
@@ -543,7 +543,6 @@
تكایه پشكنینێك بكه كه ئاخۆ كێشهیهك ههیه باسی كڕاشهكهت بكات. لهكاتی سازدانی پلیتی لێكچوو ، كات له ئێمه دهگریت كه ئێمه سهرقاڵی چارهسهركردنی ههمان كێشه دهكهیت.
سكاڵا لەسەر GitHub
لهبهرگرتنهوهی سكاڵای جۆركراو
- ئاگانامە
ناتوانرێت بهستهرهكه بناسرێتەوە. بە بەرنامەیەکی دیكه بکرێتەوە؟
خستنه نۆبهتی-خۆكاری
نۆبهتهكه لە لێدەری چالاکەوە جێگۆڕکێی دەکرێت
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 5a4091db5..fb6dcafcb 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -561,7 +561,6 @@
Fronta aktivního přehrávače bude smazána
Při přechodu z jednoho přehrávače do druhého může dojít k smazání fronty
Žádat potvrzení před vyklizením fronty
- Oznámení
Nic
Bufferovat
Promíchat
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 105646a16..efa38337d 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -344,6 +344,8 @@
Gestensteuerung für Helligkeit
Gesten verwenden, um die Helligkeit einzustellen
Aktualisierungen
+ Wiedergabebenachrichtigung
+ Konfiguriert die Benachrichtigung zum aktuell abgespielten Stream
Datei gelöscht
Aktualisierungsbenachrichtigung
Benachrichtigung bei neuer NewPipe-Version
@@ -552,7 +554,6 @@
Nie
Du kannst maximal drei Aktionen auswählen, die in der Kompaktbenachrichtigung angezeigt werden sollen!
Bearbeite jede Benachrichtigungsaktion unten, indem du darauf tippst. Wähle mithilfe der Kontrollkästchen rechts bis zu drei aus, die in der Kompaktbenachrichtigung angezeigt werden sollen
- Benachrichtigung
Konnte die angegebene URL nicht erkennen. Mit einer anderen Anwendung öffnen\?
Fünfte Aktionstaste
Vierte Aktionstaste
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 1775e1ca8..d4750c115 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -485,7 +485,6 @@
\n
\nΕνεργοποιήστε το «%1$s» στις ρυθμίσεις εάν θέλετε να το δείτε.
Λειτουργία περιορισμένης πρόσβασης του YouTube
- Ειδοποίηση
Δεν ήταν δυνατή η αναγνώριση της διεύθυνσης URL. Άνοιγμα με άλλη εφαρμογή;
Αυτόματη ουρά
Η ουρά του ενεργού αναπαραγωγού θα αντικατασταθεί
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 769272345..91df86e5c 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -514,7 +514,6 @@
Tiu ĉi filmeto havas aĝlimon.
\n
\nŜalti \"%1$s\" en la agordoj, se vi volas vidi ĝin.
- Sciigo
Malhela etoso
farbi sciigon
Alŝuto
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0c8ba0a9e..73cb3a858 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -551,7 +551,6 @@
Solo en Wi-Fi
Comenzar reproducción automáticamente — %s
Reproducir cola
- Notificación
No se pudo reconocer la URL. ¿Abrir con otra aplicación\?
Poner en cola
Cambiar de un reproductor a otro puede reemplazar la cola de reproducción
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index 107c0f624..08de77661 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -486,7 +486,6 @@
\nKui sa soovid seda näha, siis lülita seadistustest „%1$s“ sisse.
YouTube\'is leiduv „Piiratud režiim“ peidab võimaliku täiskasvanutele mõeldud sisu
Näita sisu, mis vanusepiirangu tõttu ilmselt ei sobi lastele (näiteks 18+)
- Teavitus
Sa saad kasutada vaid HTTPS-urle
Öine teema
Ei iialgi
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 5b8161bf2..88f27b920 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -549,7 +549,6 @@
Adinez mugatuta dagoen eta haurrentzako desegokia izan daitezkeen edukia erakutsi (+18 adibidez)
YouTube-ren \"Modu Murriztua\" helduentzako edukia izan daitekeen edukia ezkutatzen du
Piztu YouTube-ren \"Modu Murriztua\"
- Jakinarazpena
Ezin izan da URL-a ezagutu. Beste aplikazio batekin ireki\?
Auto-ilara
Erreprodukzio ilara aktiboa ordezkatuko da
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 58f6cef81..0b6712a69 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -544,7 +544,6 @@
فقط روی وایفای
شروع خودکار پخش — %s
پخش صف
- اعلان
نشانی قابل تشخیص نبود. با برنامه دیگری باز شود؟
صفگذاری خودکار
صف پخشکنندهٔ فعال جایگزین میشود
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index cd6777b5e..92be86373 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -545,7 +545,6 @@
Vain Wi-Fi-verkossa
Aloita toisto automaattisesti — %s
Toistojono
- Ilmoitus
Ei tunnistettu URL:ää. Avataanko toisessa sovelluksessa\?
Automaattinen jonoon lisääminen
Aktiivisen soittimen jono korvataan
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 8d27ddff9..92541f40f 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -554,7 +554,6 @@
Ajouter automatiquement à la liste de lecture
La liste de lecture du lecteur actif sera remplacée
Confirmer av. de suppr. la liste de lecture
- Notification
Rien
Chargement
Lire aléatoirement
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index f22ecff86..1cfa6c47c 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -595,7 +595,6 @@
Este vídeo ten restrición de idade.
\nDebido ás novas políticas de Youtube cos vídeos con restrición de idade, NewPipe non pode acceder ás transmisións do vídeo, polo que non pode reproducilo.
Youtube ten un \"Modo Restrinxido\" que oculta contido potencialmente só para adultos
- Notificación
URL non recoñecido. Abrir con outra aplicación\?
Mostrar metainformación
Desactíveo para ocultar a descrición do vídeo e a información adicional
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 1d054155a..de469455d 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -571,7 +571,6 @@
התור מהנגן הפעיל יוחלף
מעבר מנגן אחד למשנהו עלול להחליף את התור שלך
לבקש אישור לפני מחיקת התור
- התראה
כלום
איסוף
ערבוב
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 5f295bda8..3439b5e8c 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -465,7 +465,6 @@
यूट्यूब एक \"प्रतिबंधित मोड\" प्रदान करता है जो संभावित रूप से परिपक्व सामग्री को छुपाता है
यूट्यूब का \"प्रतिबंधित मोड\" चालू करें
बच्चों के लिए अनुपयुक्त सामग्री दिखाएं क्योंकि इसकी आयु सीमा है (जैसे 18)
- अधिसूचना
केवल HTTPS यूआरएल ही समर्थित हैं
URL की पहचान नहीं हो सकी। दूसरे ऐप से खोलें\?
ऑटोमैटिकली कतार करे
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 1f8ef42a0..1db400959 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -448,7 +448,6 @@
Albumi
Pjesme
Napravio %s
- Obavijest
Nikada
Ograniči popis preuzimanja
Koristi birač mapa sustava (SAF)
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index e19fade83..93f38692e 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -485,7 +485,6 @@
\n
\nEngedélyezd a(z) \"%1$s\" beállítást ha meg szeretnéd tekinteni.
Gyermekek számára potenciálisan nem megfelelő, korhatáros tartalom mutatása (pl. 18+)
- Értesítés
Csak HTTPS URL-ek támogatottak
Metaadatok mutatása
A jelenleg aktív lejátszási sor le lesz cserélve
diff --git a/app/src/main/res/values-hy/strings.xml b/app/src/main/res/values-hy/strings.xml
index d7da33600..7d7a8b55a 100644
--- a/app/src/main/res/values-hy/strings.xml
+++ b/app/src/main/res/values-hy/strings.xml
@@ -58,7 +58,6 @@
Մասին
Ալիքներ
Ամենը
- Ծանուցում
Տեսք
Թարմացումներ
Դիտման պատմություն
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index dddf0dd6a..6e264bf13 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -540,7 +540,6 @@
Antrean dari pemutar yang aktif akan digantikan
Beralih ke pemutar yang lain mungkin akan mengganti antrean Anda
Konfirmasi sebelum mengosongkan antrean
- Notifikasi
Tidak ada
Bufer
Aduk
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 2ee99d452..f4467cfcb 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -560,7 +560,6 @@
Buffer in corso
Nella notifica compatta è possibile visualizzare al massimo 3 azioni!
Casuale
- Notifica
Niente
Ripeti
Ridimensiona copertina alla proporzione 1:1
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index f2540f205..664389514 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -554,7 +554,6 @@
繰り返し
シャッフル
バッファリング
- 通知
YouTube は、成人向けの可能性があるコンテンツを除外する「制限付きモード」を提供しています
年齢制限 (18+ など) の理由で、子供には不適切な可能性のあるコンテンツを表示する
キューに追加
diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml
index d7ac85dd1..2ed1163f3 100644
--- a/app/src/main/res/values-kmr/strings.xml
+++ b/app/src/main/res/values-kmr/strings.xml
@@ -195,7 +195,6 @@
Dilşad
Di moda popupê de dilîzin
Di paşayê de dilîzin
- Agahdayin
Nûvekirin
Xeletkirin
Xuyabûnî
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 880c45aee..7befbb800 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -499,7 +499,6 @@
시청 기록을 지우겠습니까\?
시청 기록 지우기
재생목록 실행
- 알림
URL을 인식할 수 없습니다. 다른 앱으로 여시겠습니까\?
대기열을 비우기 전 확인하도록 합니다.
안드로이드에서 썸네일의 색상에 따라 알림 색상을 조절합니다. (지원되지 않는 기기가 있을 수 있습니다.)
diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml
index be2a8086f..883347e62 100644
--- a/app/src/main/res/values-ku/strings.xml
+++ b/app/src/main/res/values-ku/strings.xml
@@ -540,7 +540,6 @@
دەسپێکردنی کارپێکەر بەخۆکاری — %s
لێدانی ڕیز
هیچ لیستەلێدانێک نیشانە نەکراوە
- پەیام
بەستەرەکە نەناسرایەوە. لە ئەپێکیتردا بکرێتەوە؟
ڕیزبوونی خۆکار
ڕیزی لێدەری چالاک جێیدەگیرێتەوە
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index d0249568e..273702727 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -332,7 +332,6 @@
Youtube turi „apribotą režimą“ kuriame slepiamas galimai suaugusiems skirtas turinys
Įjungti YouTube „apribotą režimą“
Rodyti turinį kuris gali būti netinkamas vaikams (18+)
- Pranešimai
Atnaujinimai
Kopija jau yra
Palaikomi tik HTTPS adresai
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 58d40b61d..e7914fe29 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -262,7 +262,6 @@
Saturs
Atskaņo popup režīmā
Atskaņo fonā
- Notifikācija
Atjauninājumi
Atkļūdošana
Izskats
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index eb78ca44c..1713e7291 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -589,7 +589,6 @@
\nപ്രായ-നിയന്ത്രിത വീഡിയോകളുള്ള പുതിയ യൂട്യൂബ് നയങ്ങൾ കാരണം, ന്യൂപൈപ്പിന് അതിന്റെ വീഡിയോ സ്ട്രീമുകളിലൊന്നും ആക്സസ് ചെയ്യാൻ കഴിയില്ല, അതിനാൽ ഇത് പ്ലേ ചെയ്യാൻ കഴിയില്ല.
പക്വതയുള്ള ഉള്ളടക്കം മറയ്ക്കുന്ന \"നിയന്ത്രിത മോഡ്\" യൂട്യൂബ് നൽകുന്നു
കുട്ടികൾക്ക് അനുയോജ്യമല്ലാത്ത ഉള്ളടക്കം കാണിക്കുക കാരണം അതിന് പ്രായപരിധി ഉണ്ട് (18+ പോലെ)
- അറിയിപ്പ്
URL തിരിച്ചറിയാൻ കഴിഞ്ഞില്ല. മറ്റൊരു അപ്ലിക്കേഷൻ ഉപയോഗിച്ച് തുറക്കണോ\?
യാന്ത്രിക-ക്യൂ
സ്ട്രീം സ്രഷ്ടാവ്, സ്ട്രീം ഉള്ളടക്കം അല്ലെങ്കിൽ ഒരു തിരയൽ അഭ്യർത്ഥന എന്നിവയെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾ ഉൾക്കൊള്ളുന്ന മെറ്റാ വിവര ബോക്സുകൾ മറയ്ക്കുന്നതിന് ഓഫാക്കുക
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 76aef646f..6af6ec25d 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -384,7 +384,6 @@
- %d hari
Bantuan
- Pemberitahuan
Buka dengan
- %s pendengar
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 9cce73360..37142af1b 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -554,7 +554,6 @@
Spill kø
Ingen spillelistebokmerker enda
Kopier formatert rapport
- Merknad
Gjenta
Femte handlingstast
Fjerde handlingstast
diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml
index be7627079..0584e12e6 100644
--- a/app/src/main/res/values-nl-rBE/strings.xml
+++ b/app/src/main/res/values-nl-rBE/strings.xml
@@ -538,7 +538,6 @@
YouTube biedt een \"beperkte modes\" aan, dit verbergt mogelijk materiaal voor volwassenen
YouTube \"beperkte modus\" aanzetten
Toon inhoud die mogelijk niet geschikt is voor kinderen omwille van een leeftijdslimiet (zoals 18+)
- Melding
Kanaal bestaat al
Alleen HTTPS URL\'s worden ondersteund
Kon kanaal niet valideren
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index b835c68dc..6570e1044 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -546,7 +546,6 @@
Enkel via Wi-Fi
Start automatisch met afspelen — %s
Speel wachtrij af
- Notificatie
Kon de URL niet herkennen. In een andere app openen\?
De actieve spelerswachtrij wordt vervangen
Veranderen van één speler naar een andere kan jouw wachtrij vervangen
diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml
index 8fad329f3..f1aa81b6b 100644
--- a/app/src/main/res/values-pa/strings.xml
+++ b/app/src/main/res/values-pa/strings.xml
@@ -586,7 +586,6 @@
ਯੂਟਿਊਬ \"ਪਾਬੰਦੀਸ਼ੁਦਾ ਮੋਡ\" ਉਪਲਬਧ ਕਰਾਉਂਦਾ ਹੈ ਜੋ ਬਾਲਗਾਂ ਵਾਲ਼ੀ ਸਮੱਗਰੀ ਲੁਕਾਉਂਦਾ ਹੈ
ਯੂਟਿਊਬ ਦਾ ਪਾਬੰਦੀਸ਼ੁਦਾ ਮੋਡ ਚਾਲੂ ਕਰੋ
ਉਹ ਸਮੱਗਰੀ ਵੀ ਵਿਖਾਓ ਜੋ ਉਮਰ-ਸੀਮਾ ਕਰਕੇ ਬੱਚਿਆਂ ਲਈ ਸ਼ਾਇਦ ਸਹੀ ਨਾ ਹੋਵੇ (ਜਿਵੇਂ 18+)
- ਇਤਲਾਹਾਂ
ਸਥਿਤੀ ਪਹਿਲਾਂ ਨੂੰ ਮੌਜੂਦ ਹੈ
ਸਿਰਫ਼ HTTP URLs ਹੀ ਮਾਣਨਯੋਗ ਹਨ
ਸਥਿਤੀ ਦੀ ਜਾਇਜ਼ਗੀ ਤਸਦੀਕ ਨਹੀਂ ਹੋ ਸਕੀ
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index b4de246eb..812347ceb 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -566,7 +566,6 @@
Kolejka aktywnego odtwarzacza zostanie zastąpiona
Przejście z jednego odtwarzacza na inny może zastąpić kolejkę
Poproś o potwierdzenie przed wyczyszczeniem kolejki
- Powiadomienie
Nic
Buforowanie
Losuj
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 7c85328d0..693a33910 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -553,7 +553,6 @@
Solicitar confirmação antes de limpar uma fila
Aleatório
Carregando
- Notificação
Nada
Repetir
Selecione no máximo três botões para a notificação compacta!
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 4fa8e4e18..8b15b674e 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -545,7 +545,6 @@
Apenas em Wi-Fi
Iniciar reprodução automaticamente — %s
Reproduzir fila
- Notificação
URL não reconhecido. Abrir com outra aplicação\?
Colocar na fila automaticamente
A fila do reprodutor ativo será substituída
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index c1d95a797..ddce62f46 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -556,7 +556,6 @@
URL não reconhecido. Abrir com outra aplicação\?
Colocar na fila automaticamente
Baralhar
- Notificação
Apenas em Wi-Fi
Nada
Mudar de um reprodutor para outro pode substituir a sua fila
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 19bd15385..89540e819 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -365,7 +365,6 @@
YouTube oferă un \"Mod restricționat\" care ascunde conținutul potențial matur
Activați \"Modul restricționat\" de pe YouTube
Afișați conținut posibil nepotrivit pentru copii, deoarece are o limită de vârstă (cum ar fi 18+)
- Notificare
Adresa URL nu a putut fi recunoscută. Deschideți cu o altă aplicație\?
Afișează informațiile meta
Faceți ca Android să personalizeze culoarea notificării în funcție de culoarea principală din miniatură (rețineți că aceasta nu este disponibilă pe toate dispozitivele)
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 7e16d5858..d351b25f2 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -568,8 +568,7 @@
Очередь активного плеера будет заменена
Подтверждать очистку очереди
Переход от одного плеера к другому может заменить вашу очередь
- Уведомление
- Настроить уведомление о воспроизводимом сейчас потоке
+ Настроить уведомление о воспроизводимом сейчас потоке
Ничего
Буферизация
Перемешать
diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml
index 1afb7990f..c35d299df 100644
--- a/app/src/main/res/values-sc/strings.xml
+++ b/app/src/main/res/values-sc/strings.xml
@@ -550,7 +550,6 @@
Sa lista dae su riproduidore ativu at a èssere remplasada
Colende dae unu riproduidore a s\'àteru dias pòdere remplasare sa lista tua
Pedi una cunfirma in antis de iscantzellare una lista
- Notìfica
Òrdine casuale
Modìfica cada atzione de notìfica inoghe in suta incarchende·la. Ischerta·nde finas a tres de ammustrare in sa notìfica cumpata impreende sas casellas de controllu a destra
Iscala sa miniadura ammustrada in sa notìfica dae su formadu in 16:9 a cussu 1:1 (diat pòdere causare istorchimentos)
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 3df4ff0af..ae2e94f42 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -556,7 +556,6 @@
Zatiaľ bez záložiek zoznamu
Vyberte zoznam skladieb
Skontrolujte prosím, či rovnaká chyba už nie je nahlásená. Vytváranie duplicitných hlásení komplikuje prácu vývojárov.
- Oznámenia
Nemožno rozpoznať URL. Otvoriť pomocou inej aplikácie\?
Automatický rad
Zoznam aktuálneho prehrávača bude prepísaný
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index dbfef543d..e9afcee70 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -427,7 +427,6 @@
Youtube ponuja \"omejeni način\", ki skrije potencialno vsebino za odrasle
Vklop YouTubovega \"omejenega načina\"
Prikaz vsebin, ki so morda neprimerne za otroke zaradi omejitve starosti (kot na primer 18+)
- Obvestilo
Instanca že obstaja
Validacija instance ni bila mogoča
Vnesite URL instance
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index bf9e7a768..a113094da 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -362,7 +362,6 @@
Luuqada & Fadhiga Kale
Ku daaraya daaqada
Ka daaraya xaga dambe
- Ogaysiisyada
Cusboonaysiinta
Cilad bixinta
Nashqada
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 10ab40e69..d8bcb6fd1 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -546,7 +546,6 @@
Kurrë
Nise luajtjen automatikisht — %s
Lista e luajtjes
- Njoftim
Nuk u njoh URL. Të hapet me një aplikacion tjetër\?
Listë automatike luajtjeje
Lista aktive e luajtjes do të zëvendësohet
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index e27d9e8a8..79020f28a 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -486,7 +486,6 @@
Јутјуб омогућава „Ограничени режим“ који скрива потенцијални садржај за одрасле
Укључити Јутјубов „Ограничени режим“
Приказ садржаја који можда није прикладан за децу јер има старосну границу (попут 18+)
- Обавештење
Ажурирања
Инстанца већ постоји
Подржане су само HTTPS УРЛ адресе
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 4fc9778be..30fb72a2a 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -531,7 +531,6 @@
Skala videominiatyrbilden som visas i aviseringen från 16:9- till 1:1-förhållande (kan orsaka bildförvrängning)
Starta uppspelning automatiskt — %s
Uppspelningskö
- Aviseringar
Kunde inte känna igen URL:en. Vill du öppna med annan app\?
Köa automatiskt
Den aktiva spellistan kommer att ersättas
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index f318de744..d8e8c7bcc 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -551,7 +551,6 @@
Etkin oynatıcının kuyruğu değiştirilecek
Bir oynatıcıdan diğerine geçmek kuyruğunuzu değiştirebilir
Bir kuyruğu temizlemeden önce onay iste
- Bildirim
Hiçbir şey
Ara belleğe alınıyor
Karıştır
diff --git a/app/src/main/res/values-tzm/strings.xml b/app/src/main/res/values-tzm/strings.xml
index d53dcec8b..2dba77eb9 100644
--- a/app/src/main/res/values-tzm/strings.xml
+++ b/app/src/main/res/values-tzm/strings.xml
@@ -134,7 +134,6 @@
Tagamin
Tagamin
Usrid
- Tineɣmisin
Tisdɣiwin
Ameɣri
Agem
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index ecfbb07ff..acea873f2 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -581,8 +581,7 @@
Опис
Повʼязані елементи
Коментарі
- Сповіщення
- Налаштувати повідомлення про відтворюваний наразі потік
+ Налаштувати повідомлення про відтворюваний наразі потік
Не розпізнано URL. Відкрити через іншу програму\?
Самододавання в чергу
Показувати метадані
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 1f427504b..ba43cdd61 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -474,7 +474,6 @@
یوٹیوب ایک \"پابندی والا وضع\" فراہم کرتا ہے جو امکانی طور پر نازیبا مواد کو چھپاتا ہے
یوٹیوب کا \"پابندی والا وضع\" چالو کریں
وہ مواد دکھائیں جو بچوں کے لیے ممکنہ طور پر نا مناسب ہیں کیوں کہ اس میں عمر کی حد ہے (جیسے 18+)
- اطلاع
URL کو نہیں پہچان سکے۔ کسی اور ایپ کے ساتھ کھولیں؟
ازخود قطار
اسٹریم کے موجد، اسٹریم مواد یا تلاش کی درخواست کے بارے میں اضافی معلومات والے میٹا انفارمیشن بکسوں کو چھپانے کیلئے بند کریں۔
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 3635aa4b4..803e22f6a 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -534,7 +534,6 @@
Vui lòng kiểm tra xem vấn đề bạn đang gặp đã có báo cáo trước đó chưa. Nếu bạn tạo nhiều báo cáo trùng lặp, bạn sẽ làm tốn thời gian để chúng tôi đọc thay vì thực sự sửa lỗi.
Báo cáo trên GitHub
Sao chép bản báo cáo đã được định dạng
- Thông báo
Không thể đọc URL này. Mở với app khác\?
Tự động thêm vào hàng đợi
Hàng đợi của trình phát hiện tại sẽ bị thay thế
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index e5c499119..28840775d 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -541,7 +541,6 @@
作用中播放器的佇列可能會被取代
從一個播放器切換到另一個可能會取代您的佇列
清除佇列前要求確認
- 通知
沒有東西
正在緩衝
隨機播放
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e67016142..e8ac4f1b8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -144,8 +144,8 @@
Appearance
Debug
Updates
- Notification
- Configure current playing stream notification
+ Player-Notification
+ Configure current playing stream notification
Playing in background
Playing in popup mode
Content
diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml
index 6bc9f3381..cbe1d4c24 100644
--- a/app/src/main/res/xml/appearance_settings.xml
+++ b/app/src/main/res/xml/appearance_settings.xml
@@ -23,6 +23,12 @@
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
+
+
+
+
-
-
-
-