From 2ac0d1f13a808a377fd9b8ebaa4e416d0059809c Mon Sep 17 00:00:00 2001
From: Christian Schabesberger
Date: Tue, 2 Jul 2024 09:31:34 +0200
Subject: [PATCH 01/93] add NP icon for Android Studio's NewUI
---
.idea/icon.svg | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 .idea/icon.svg
diff --git a/.idea/icon.svg b/.idea/icon.svg
new file mode 100644
index 000000000..51fdf95de
--- /dev/null
+++ b/.idea/icon.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 9f766ebf781b770b6e1b37da084e6193698cee4c Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 11 Jul 2024 09:30:22 +0200
Subject: [PATCH 02/93] Fix NPE in MediaSessionPlayerUi while destroying player
---
.../newpipe/player/mediasession/MediaSessionPlayerUi.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java
index 737ebc5dd..c673e688c 100644
--- a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java
@@ -38,7 +38,9 @@ public class MediaSessionPlayerUi extends PlayerUi
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "MediaSessUi";
+ @Nullable
private MediaSessionCompat mediaSession;
+ @Nullable
private MediaSessionConnector sessionConnector;
private final String ignoreHardwareMediaButtonsKey;
@@ -198,6 +200,11 @@ public class MediaSessionPlayerUi extends PlayerUi
return;
}
+ if (sessionConnector == null) {
+ // sessionConnector will be null after destroyPlayer is called
+ return;
+ }
+
// only use the fourth and fifth actions (the settings page also shows only the last 2 on
// Android 13+)
final List newNotificationActions = IntStream.of(3, 4)
From 0e0cee1bcecc0414a25b88ff0b153273c6a9d546 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 11 Jul 2024 23:27:26 +0200
Subject: [PATCH 03/93] Update NewPipeExtractor to v0.24.1
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 28a208195..eebf9135f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -198,7 +198,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.0'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.1'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
From acc5be92ac0fcf1899ac265575b01349f4768d82 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 11 Jul 2024 23:39:53 +0200
Subject: [PATCH 04/93] Add changelogs for hotfix release v0.27.1 (998)
---
fastlane/metadata/android/ar/changelogs/998.txt | 1 +
fastlane/metadata/android/az/changelogs/998.txt | 1 +
fastlane/metadata/android/cs/changelogs/998.txt | 1 +
fastlane/metadata/android/de/changelogs/998.txt | 1 +
fastlane/metadata/android/en-US/changelogs/998.txt | 4 ++++
fastlane/metadata/android/es/changelogs/998.txt | 1 +
fastlane/metadata/android/fa/changelogs/998.txt | 1 +
fastlane/metadata/android/fr/changelogs/998.txt | 1 +
fastlane/metadata/android/he/changelogs/998.txt | 1 +
fastlane/metadata/android/hi/changelogs/998.txt | 1 +
fastlane/metadata/android/id/changelogs/998.txt | 1 +
fastlane/metadata/android/it/changelogs/998.txt | 4 ++++
fastlane/metadata/android/ka/changelogs/998.txt | 1 +
fastlane/metadata/android/nl/changelogs/998.txt | 1 +
fastlane/metadata/android/pa/changelogs/998.txt | 1 +
fastlane/metadata/android/pt-BR/changelogs/998.txt | 1 +
fastlane/metadata/android/pt-PT/changelogs/998.txt | 1 +
fastlane/metadata/android/pt/changelogs/998.txt | 1 +
fastlane/metadata/android/ru/changelogs/998.txt | 1 +
fastlane/metadata/android/sv/changelogs/998.txt | 1 +
fastlane/metadata/android/tr/changelogs/998.txt | 1 +
fastlane/metadata/android/uk/changelogs/998.txt | 1 +
fastlane/metadata/android/vi/changelogs/998.txt | 1 +
fastlane/metadata/android/zh-Hans/changelogs/998.txt | 1 +
fastlane/metadata/android/zh-Hant/changelogs/998.txt | 1 +
fastlane/metadata/android/zh_Hant_HK/changelogs/998.txt | 1 +
26 files changed, 32 insertions(+)
create mode 100644 fastlane/metadata/android/ar/changelogs/998.txt
create mode 100644 fastlane/metadata/android/az/changelogs/998.txt
create mode 100644 fastlane/metadata/android/cs/changelogs/998.txt
create mode 100644 fastlane/metadata/android/de/changelogs/998.txt
create mode 100644 fastlane/metadata/android/en-US/changelogs/998.txt
create mode 100644 fastlane/metadata/android/es/changelogs/998.txt
create mode 100644 fastlane/metadata/android/fa/changelogs/998.txt
create mode 100644 fastlane/metadata/android/fr/changelogs/998.txt
create mode 100644 fastlane/metadata/android/he/changelogs/998.txt
create mode 100644 fastlane/metadata/android/hi/changelogs/998.txt
create mode 100644 fastlane/metadata/android/id/changelogs/998.txt
create mode 100644 fastlane/metadata/android/it/changelogs/998.txt
create mode 100644 fastlane/metadata/android/ka/changelogs/998.txt
create mode 100644 fastlane/metadata/android/nl/changelogs/998.txt
create mode 100644 fastlane/metadata/android/pa/changelogs/998.txt
create mode 100644 fastlane/metadata/android/pt-BR/changelogs/998.txt
create mode 100644 fastlane/metadata/android/pt-PT/changelogs/998.txt
create mode 100644 fastlane/metadata/android/pt/changelogs/998.txt
create mode 100644 fastlane/metadata/android/ru/changelogs/998.txt
create mode 100644 fastlane/metadata/android/sv/changelogs/998.txt
create mode 100644 fastlane/metadata/android/tr/changelogs/998.txt
create mode 100644 fastlane/metadata/android/uk/changelogs/998.txt
create mode 100644 fastlane/metadata/android/vi/changelogs/998.txt
create mode 100644 fastlane/metadata/android/zh-Hans/changelogs/998.txt
create mode 100644 fastlane/metadata/android/zh-Hant/changelogs/998.txt
create mode 100644 fastlane/metadata/android/zh_Hant_HK/changelogs/998.txt
diff --git a/fastlane/metadata/android/ar/changelogs/998.txt b/fastlane/metadata/android/ar/changelogs/998.txt
new file mode 100644
index 000000000..562f16944
--- /dev/null
+++ b/fastlane/metadata/android/ar/changelogs/998.txt
@@ -0,0 +1 @@
+تم إصلاح YouTube الذي لا يقوم بتشغيل أي دفق
diff --git a/fastlane/metadata/android/az/changelogs/998.txt b/fastlane/metadata/android/az/changelogs/998.txt
new file mode 100644
index 000000000..16a2e1013
--- /dev/null
+++ b/fastlane/metadata/android/az/changelogs/998.txt
@@ -0,0 +1 @@
+YouTube-un heç bir yayım oynatmaması düzəldildi
diff --git a/fastlane/metadata/android/cs/changelogs/998.txt b/fastlane/metadata/android/cs/changelogs/998.txt
new file mode 100644
index 000000000..7035a1112
--- /dev/null
+++ b/fastlane/metadata/android/cs/changelogs/998.txt
@@ -0,0 +1 @@
+Opraveno nepřehrávání jakéhokoli streamu ve službě YouTube
diff --git a/fastlane/metadata/android/de/changelogs/998.txt b/fastlane/metadata/android/de/changelogs/998.txt
new file mode 100644
index 000000000..43623578f
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/998.txt
@@ -0,0 +1 @@
+Behoben, dass YouTube keinen Stream abspielte
diff --git a/fastlane/metadata/android/en-US/changelogs/998.txt b/fastlane/metadata/android/en-US/changelogs/998.txt
new file mode 100644
index 000000000..468df0204
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/998.txt
@@ -0,0 +1,4 @@
+Fixed YouTube not playing any stream because of HTTP 403 errors.
+
+Occasional HTTP 403 errors in the middle of a YouTube video are not fixed yet.
+That issue will be addressed in another hotfix release as soon as possible.
diff --git a/fastlane/metadata/android/es/changelogs/998.txt b/fastlane/metadata/android/es/changelogs/998.txt
new file mode 100644
index 000000000..80b4efa55
--- /dev/null
+++ b/fastlane/metadata/android/es/changelogs/998.txt
@@ -0,0 +1 @@
+Arreglo en YouTube no reproduciendo flujos
diff --git a/fastlane/metadata/android/fa/changelogs/998.txt b/fastlane/metadata/android/fa/changelogs/998.txt
new file mode 100644
index 000000000..ba5413d49
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/998.txt
@@ -0,0 +1 @@
+مشکل عدم نمایش پخشزنده برطرف شد
diff --git a/fastlane/metadata/android/fr/changelogs/998.txt b/fastlane/metadata/android/fr/changelogs/998.txt
new file mode 100644
index 000000000..3ad3bf279
--- /dev/null
+++ b/fastlane/metadata/android/fr/changelogs/998.txt
@@ -0,0 +1 @@
+Correction de YouTube qui ne lisait aucun média
diff --git a/fastlane/metadata/android/he/changelogs/998.txt b/fastlane/metadata/android/he/changelogs/998.txt
new file mode 100644
index 000000000..50731171e
--- /dev/null
+++ b/fastlane/metadata/android/he/changelogs/998.txt
@@ -0,0 +1 @@
+תוקנה התקלה ש־YouTube לא מנגן אף תזרים
diff --git a/fastlane/metadata/android/hi/changelogs/998.txt b/fastlane/metadata/android/hi/changelogs/998.txt
new file mode 100644
index 000000000..071ab64e3
--- /dev/null
+++ b/fastlane/metadata/android/hi/changelogs/998.txt
@@ -0,0 +1 @@
+फिक्स्ड YouTube कोई स्ट्रीम नहीं चला रहा है
diff --git a/fastlane/metadata/android/id/changelogs/998.txt b/fastlane/metadata/android/id/changelogs/998.txt
new file mode 100644
index 000000000..d3fea84ab
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/998.txt
@@ -0,0 +1 @@
+Memperbaiki YouTube yang tidak memutar streaming apa pun
diff --git a/fastlane/metadata/android/it/changelogs/998.txt b/fastlane/metadata/android/it/changelogs/998.txt
new file mode 100644
index 000000000..73fc6f2d8
--- /dev/null
+++ b/fastlane/metadata/android/it/changelogs/998.txt
@@ -0,0 +1,4 @@
+Corretto problema di riproduzione di YouTube causato da errori HTTP 403.
+
+Gli errori HTTP 403 saltuari nel mezzo di un video di YouTube non sono ancora stati sistemati.
+Questo problema sarà affrontato in un altra release hotfix non appena possibile.
diff --git a/fastlane/metadata/android/ka/changelogs/998.txt b/fastlane/metadata/android/ka/changelogs/998.txt
new file mode 100644
index 000000000..d20512f17
--- /dev/null
+++ b/fastlane/metadata/android/ka/changelogs/998.txt
@@ -0,0 +1 @@
+გაასწორა YouTube არ უკრავს არცერთ ნაკადს
diff --git a/fastlane/metadata/android/nl/changelogs/998.txt b/fastlane/metadata/android/nl/changelogs/998.txt
new file mode 100644
index 000000000..9bd8adf86
--- /dev/null
+++ b/fastlane/metadata/android/nl/changelogs/998.txt
@@ -0,0 +1 @@
+YouTube speelt geen stream af opgelost
diff --git a/fastlane/metadata/android/pa/changelogs/998.txt b/fastlane/metadata/android/pa/changelogs/998.txt
new file mode 100644
index 000000000..fe62a1330
--- /dev/null
+++ b/fastlane/metadata/android/pa/changelogs/998.txt
@@ -0,0 +1 @@
+ਸਥਿਰ YouTube ਕੋਈ ਸਟ੍ਰੀਮ ਨਹੀਂ ਚਲਾ ਰਿਹਾ
diff --git a/fastlane/metadata/android/pt-BR/changelogs/998.txt b/fastlane/metadata/android/pt-BR/changelogs/998.txt
new file mode 100644
index 000000000..59fc6a5cd
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/998.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir qualquer transmissão
diff --git a/fastlane/metadata/android/pt-PT/changelogs/998.txt b/fastlane/metadata/android/pt-PT/changelogs/998.txt
new file mode 100644
index 000000000..93519d64d
--- /dev/null
+++ b/fastlane/metadata/android/pt-PT/changelogs/998.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir nenhuma transmissão
diff --git a/fastlane/metadata/android/pt/changelogs/998.txt b/fastlane/metadata/android/pt/changelogs/998.txt
new file mode 100644
index 000000000..93519d64d
--- /dev/null
+++ b/fastlane/metadata/android/pt/changelogs/998.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir nenhuma transmissão
diff --git a/fastlane/metadata/android/ru/changelogs/998.txt b/fastlane/metadata/android/ru/changelogs/998.txt
new file mode 100644
index 000000000..d3978869d
--- /dev/null
+++ b/fastlane/metadata/android/ru/changelogs/998.txt
@@ -0,0 +1 @@
+Исправлено: YouTube не воспроизводил никакие потоки
diff --git a/fastlane/metadata/android/sv/changelogs/998.txt b/fastlane/metadata/android/sv/changelogs/998.txt
new file mode 100644
index 000000000..35f298dbf
--- /dev/null
+++ b/fastlane/metadata/android/sv/changelogs/998.txt
@@ -0,0 +1 @@
+Åtgärdat att YouTube inte spelar någon stream
diff --git a/fastlane/metadata/android/tr/changelogs/998.txt b/fastlane/metadata/android/tr/changelogs/998.txt
new file mode 100644
index 000000000..e5979c68d
--- /dev/null
+++ b/fastlane/metadata/android/tr/changelogs/998.txt
@@ -0,0 +1 @@
+YouTube'un herhangi bir akışı oynatmaması düzeltildi
diff --git a/fastlane/metadata/android/uk/changelogs/998.txt b/fastlane/metadata/android/uk/changelogs/998.txt
new file mode 100644
index 000000000..905287c74
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/998.txt
@@ -0,0 +1 @@
+Виправлено проблему невідтворюваності трансляцій
diff --git a/fastlane/metadata/android/vi/changelogs/998.txt b/fastlane/metadata/android/vi/changelogs/998.txt
new file mode 100644
index 000000000..d2086b62c
--- /dev/null
+++ b/fastlane/metadata/android/vi/changelogs/998.txt
@@ -0,0 +1 @@
+Đã sửa lỗi YouTube không phát bất kỳ luồng nào
diff --git a/fastlane/metadata/android/zh-Hans/changelogs/998.txt b/fastlane/metadata/android/zh-Hans/changelogs/998.txt
new file mode 100644
index 000000000..8a5424c9e
--- /dev/null
+++ b/fastlane/metadata/android/zh-Hans/changelogs/998.txt
@@ -0,0 +1 @@
+修复YouTube无法播放任何视频
diff --git a/fastlane/metadata/android/zh-Hant/changelogs/998.txt b/fastlane/metadata/android/zh-Hant/changelogs/998.txt
new file mode 100644
index 000000000..4e8bf6537
--- /dev/null
+++ b/fastlane/metadata/android/zh-Hant/changelogs/998.txt
@@ -0,0 +1 @@
+修正 YouTube 無法播放任何串流
diff --git a/fastlane/metadata/android/zh_Hant_HK/changelogs/998.txt b/fastlane/metadata/android/zh_Hant_HK/changelogs/998.txt
new file mode 100644
index 000000000..9a4721551
--- /dev/null
+++ b/fastlane/metadata/android/zh_Hant_HK/changelogs/998.txt
@@ -0,0 +1 @@
+修正咗 YouTube 乜嘢實況串流都播唔到嘅問題
From 0f64158469d42a37df5c8ef200f7963cafabe361 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 11 Jul 2024 23:41:53 +0200
Subject: [PATCH 05/93] Hotfix release v0.27.1 (998)
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index eebf9135f..397a7301e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,8 +20,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdk 21
targetSdk 33
- versionCode 997
- versionName "0.27.0"
+ versionCode 998
+ versionName "0.27.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
From ad6b676c81ad0beca27a287bb2d0b5f8b41f8f20 Mon Sep 17 00:00:00 2001
From: #27 <68751594+tag27@users.noreply.github.com>
Date: Sat, 13 Jul 2024 14:32:24 -0300
Subject: [PATCH 06/93] Update README.pt_BR.md (#11275)
---
doc/README.pt_BR.md | 128 +++++++++++++++++++++++---------------------
1 file changed, 66 insertions(+), 62 deletions(-)
diff --git a/doc/README.pt_BR.md b/doc/README.pt_BR.md
index 01fa718ad..59ff65db3 100644
--- a/doc/README.pt_BR.md
+++ b/doc/README.pt_BR.md
@@ -1,3 +1,6 @@
+Nós estamos planejando reescrever grandes pedaços do código base, para gerar um novo, moderno e estável NewPipe !
+Por favor, não abra solicitações de pull para novos recursos por enquanto, apenas correções de bugs serão aceitas.
+
NewPipe
@@ -13,16 +16,16 @@
-Screenshots • Descrição • Características • Atualizações • Contribuição • Doar • Licença
+Screenshots • Serviços Suportados • Descrição • Recursos • Instalação e atualizações • Contribuições • Doar • Licença
Site • Blog • FAQ • Press
-*Read this document in other languages: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)*
+*Leia esse documento em outras línguas: [Deutsch](README.de.md), [English](../README.md), [Español](README.es.md), [Français](README.fr.md), [हिन्दी](README.hi.md), [Italiano](README.it.md), [한국어](README.ko.md), [Português Brasil](README.pt_BR.md), [Polski](README.pl.md), [ਪੰਜਾਬੀ ](README.pa.md), [日本語](README.ja.md), [Română](README.ro.md), [Soomaali](README.so.md), [Türkçe](README.tr.md), [正體中文](README.zh_TW.md), [অসমীয়া](README.asm.md), [うちなーぐち](README.ryu.md), [Српски](README.sr.md)*
> [!warning]
-> ESTA É UMA VERSÃO BETA, PORTANTO, VOCÊ PODE ENCONTRAR BUGS. ENCONTROU ALGUM, ABRA UM ISSUE ATRAVÉS DO NOSSO REPOSITÓRIO GITHUB.
+> ESTA É UMA VERSÃO BETA, PORTANTO, VOCÊ PODE ENCONTRAR BUGS. ENCONTROU ALGUM, ABRA UM ISSUE ATRAVÉS DO NOSSO REPOSITÓRIO GITHUB PREENCHENDO O MODELO.
>
-> COLOCAR NEWPIPE OU QUALQUER FORK DELE NA GOOGLE PLAY STORE VIOLA SEUS TERMOS E CONDIÇÕES.
+> COLOCAR NEWPIPE, OU QUALQUER FORK DELE, NA GOOGLE PLAY STORE VIOLA SEUS TERMOS E CONDIÇÕES.
## Screenshots
@@ -39,83 +42,84 @@
[ ](../fastlane/metadata/android/en-US/images/tenInchScreenshots/09.png)
[ ](../fastlane/metadata/android/en-US/images/tenInchScreenshots/10.png)
-## Descrição
-
-O NewPipe não usa nenhuma biblioteca de framework do Google, nem a API do YouTube. Os sites são apenas analisados para obter informações necessárias, para que este aplicativo possa ser usado em dispositivos sem os serviços do Google instalados. Além disso, você não precisa de uma conta no YouTube para usar o NewPipe, que é um software livre com copyleft.
-
-### Características
-
-* Procurar vídeos
-* Exibir informações gerais sobre vídeos
-* Assista aos vídeos do YouTube
-* Ouça vídeos do YouTube
-* Modo popup (player flutuante)
-* Selecione o player para assistir streaming
-* Baixar vídeos
-* Baixar somente áudio
-* Abrir vídeo no Kodi
-* Mostrar vídeos próximos/relacionados
-* Pesquise no YouTube em um idioma específico
-* Assistir/Bloquear material restrito
-* Exibir informações gerais sobre canais
-* Pesquisar canais
-* Assista a vídeos de um canal
-* Suporte Orbot/Tor (ainda não diretamente)
-* Suporte 1080p/2K/4K
-* Ver histórico
-* Inscreva-se nos canais
-* Procurar histórico
-* Porcurar/Assistir playlists
-* Assistir playlists em fila
-* Vídeos em fila
-* Playlists Local
-* Legenda
-* Suporte a live
-* Mostrar comentários
-
### Serviços Suportados
-O NewPipe suporta vários serviços. Nosso [documentação](https://teamnewpipe.github.io/documentation/) fornecer mais informações sobre como um novo serviço pode ser adicionado ao aplicativo e ao extrator. Por favor, entre em contato conosco se você pretende adicionar um novo. Atualmente, os serviços suportados são:
+Atualmente, os serviços suportados são:
-* YouTube
-* SoundCloud \[beta\]
-* media.ccc.de \[beta\]
-* PeerTube instances \[beta\]
-* Bandcamp \[beta\]
+* YouTube ([site](https://www.youtube.com/)) e YouTube Music ([site](https://music.youtube.com/)) ([wiki](https://en.wikipedia.org/wiki/YouTube))
+* PeerTube ([site](https://joinpeertube.org/)) e todas suas instâncias (abra o site para saber o que isso significa!) ([wiki](https://en.wikipedia.org/wiki/PeerTube))
+* Bandcamp ([site](https://bandcamp.com/)) ([wiki](https://en.wikipedia.org/wiki/Bandcamp))
+* SoundCloud ([site](https://soundcloud.com/)) ([wiki](https://en.wikipedia.org/wiki/SoundCloud))
+* media.ccc.de ([site](https://media.ccc.de/)) ([wiki](https://en.wikipedia.org/wiki/Chaos_Computer_Club))
-## Atualizações
-Quando uma alteração no código NewPipe (devido à adição de recursos ou fixação de bugs), eventualmente ocorrerá uma versão. Estes estão no formato x.xx.x . A fim de obter esta nova versão, você pode:
- 1. Construa um APK de depuração você mesmo. Esta é a maneira mais rápida de obter novos recursos em seu dispositivo, mas é muito mais complicado, por isso recomendamos usar um dos outros métodos.
- 2. Adicione nosso repo personalizado ao F-Droid e instale-o a partir daí assim que publicarmos um lançamento. As instruções estão aqui.: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
- 3. Baixe o APK do [GitHub Releases](https://github.com/TeamNewPipe/NewPipe/releases) e instalá-lo assim que publicarmos um lançamento.
- 4. Atualização via F-droid. Este é o método mais lento para obter atualizações, pois o F-Droid deve reconhecer alterações, construir o próprio APK, assiná-lo e, em seguida, enviar a atualização para os usuários.
+Como você pode ver, o NewPipe suporta múltiplos serviços de vídeo e áudio. Embora tenha começado com o YouTube, outras pessoas adicionaram mais serviços ao longo dos anos, tornando o NewPipe cada vez mais versátil!
-Recomendamos o método 2 para a maioria dos usuários. Os APKs instalados usando o método 2 ou 3 são compatíveis entre si, mas não com aqueles instalados usando o método 4. Isso se deve à mesma chave de assinatura (nossa) sendo usada para 2 e 3, mas uma chave de assinatura diferente (F-Droid's) está sendo usada para 4. Construir um APK depuração usando o método 1 exclui totalmente uma chave. Assinar chaves ajudam a garantir que um usuário não seja enganado para instalar uma atualização maliciosa em um aplicativo.
+Parcialmente devido as circustâncias e a sua popularidade, o YouTube tem o melhor suporte em relação a esses serviços. Se você usa ou é familarizado com qualquer um desses serviços, por favor ajude-nos a melhorar o suporte para eles! Estamos procurando mantenedores para o SoundCloud e o PeerTube.
+
+Se você pretende adicionar um novo serviço, por favor entre em contato conosco primeiro! Nossa [documentação](https://teamnewpipe.github.io/documentation/) traz mais informações em como um novo serviço pode ser adicionado ao aplicativo e no [NewPipe Extractor](https://github.com/TeamNewPipe/NewPipeExtractor).
+
+## Descrição
+
+NewPipe funciona buscando os dados necessários da API oficial (ex. PeerTube) ou do serviço que você está usando. Se a API oficial é restrita (ex. YouTube) para nossos propósitos, ou é proprietária, o aplicativo analisa o site ou usa uma API interna. Isso significa que não é preciso ter uma conta de qualquer serviço para usar o NewPipe.
+
+Também, desde que somos um software livre e de código aberto, nem o aplicativo e nem o Extractor usa qualquer biblioteca ou framework proprietário, como o Google Play Services. Isso significa que você pode usar o NewPipe em dispositivos ou ROMs customizadas em que não tem os aplicativos do Google instalados.
+
+### Recursos
+
+* Assistir vídeos em resoluções de até 4K
+* Escutar o áudio em segundo plano, carregando apenas o fluxo de áudio para salvar dados
+* Modo popup (player flutuante, aka Picture-in-Picture)
+* Assista a transmissões ao vivo
+* Mostrar/esconder legendas/closed captions
+* Buscar vídeos e áudios (no YouTube, você pode especificar o conteúdo da linguagem também)
+* Enfileirar vídeos (e opcionalmente salvar eles como playlists locais)
+* Mostrar/esconder informações gerais sobre os vídeos (como descrições e tags)
+* Mostrar/esconder vídeos próximos/relacionados
+* Mostrar/esconder comentários
+* Buscar vídeos, áudios, canais, playlists e álbuns
+* Navegar vídeos e áudios dentro de um canal
+* Inscrever-se a canais (sim, mesmo se não estiver logado a qualquer conta!)
+* Receba notificações sobre novos vídeos de canais em que você está inscrito
+* Crie e edite grupos de canais (para facilitar a navegação e o gerenciamento)
+* Navege feeds de vídeo gerados a partir dos seus grupos de canais
+* Veja e pesquise seu histórico de vídeos
+* Pesquise e assista playlists (Eles são playlists remotas, o que significa que eles serão obtidos do serviço que você está navegando)
+* Crie e edite playlists locais (Eles são criados e salvos no aplicativo, e não são relacionados com nenhum serviço)
+* Baixe vídeos/áudios/legendas (closed captions)
+* Abra no Kodi
+* Assista/Bloqueie material restrito
+
+## Instalação e atualizações
+Você pode instalar NewPipe com um dos seguintes métodos:
+ 1. Adicione nosso repo personalizado ao F-Droid e instale-o a partir daí. As instruções estão aqui: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
+ 2. Baixe o APK aqui no [GitHub Releases](https://github.com/TeamNewPipe/NewPipe/releases) e instalá-lo assim que publicarmos um lançamento.
+ 3. Atualização via F-droid. Este é o método mais lento para obter atualizações, pois o F-Droid deve reconhecer alterações, construir o próprio APK, assiná-lo e, em seguida, enviar a atualização para os usuários.
+ 4. Construa um APK de depuração você mesmo. Esta é a maneira mais rápida de obter novos recursos em seu dispositivo, mas é muito mais complicado, por isso recomendamos usar um dos outros métodos.
+ 5. Se você estiver interessado em um recurso específico ou uma correção de bug fornecido em uma solicitação de Pull nesse repositório, pode instalar o APK a partir de lá. Leia a descrição da solicitação para instruções. A grande vantagem dos APKs específicos de S.P é que eles são instalados lado a lado com o aplicativo oficial, então você não precisa se preocupar em perder seus dados ou estragar alguma coisa.
+
+Recomendamos o método 1 para a maioria dos usuários. Os APKs instalados usando o método 1 ou 2 são compatíveis entre si (o que significa que se você instalou o NewPipe usando o método 1 ou 2, você também pode atualizar o NewPipe usando o outro), mas não com aqueles instalados usando o método 3. Isso se deve à mesma chave de assinatura (nossa) sendo usada para 1 e 2, mas uma chave de assinatura diferente (F-Droid's) está sendo usada para 3. Construir um APK depuração usando o método 4 exclui totalmente uma chave. Assinar chaves ajudam a garantir que um usuário não seja enganado para instalar uma atualização maliciosa em um aplicativo. Ao usar o método 5, cada APK é assinado com uma chave aleatória diferente fornecida pelo GitHub Actions, portanto você não pode nem mesmo atualizá-lo. Você terá que fazer backup e restaurar os dados do aplicativo sempre que desejar usar um novo APK.
Enquanto isso, se você quiser trocar de fontes por algum motivo (por exemplo, a funcionalidade principal do NewPipe foi quebrada e o F-Droid ainda não tem a atualização), recomendamos seguir este procedimento:
-1. Faça backup de seus dados através de Configurações > Conteúdo > Exportar Base de Dados para que você mantenha seu histórico, inscrições e playlists
+1. Faça backup de seus dados através de Configurações > Backup e Restauração > Exportar Base de Dados para que você mantenha seu histórico, inscrições e playlists
2. Desinstale o NewPipe
3. Baixe o APK da nova fonte e instale-o
-4. Importe os dados da etapa 1 via Configurações > Conteúdo > Inportar Banco de Dados
+4. Importe os dados da etapa 1 via Configurações > Backup e Restauração > Importar Base de Dados
-## Contribuição
-Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda.
-Quanto mais for feito, melhor fica!
-
-Se você quiser se envolver, verifique nossa [notas de contribuição](../.github/CONTRIBUTING.md).
+## Contribuições
+Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda. O aplicativo fica cada vez melhor a cada contribuição, não importa quão grande ou pequena! Se você quiser se envolver, verifique nossas [notas de contribuição](.github/CONTRIBUTING.md).
-
+
## Doar
-Se você gosta de NewPipe, ficaríamos felizes com uma doação. Você pode enviar bitcoin ou doar via Bountysource ou Liberapay. Para obter mais informações sobre como doar para a NewPipe, visite nosso [site](https://newpipe.net/donate).
+Se você gosta do NewPipe, pode enivar uma doação. Nós preferimos Liberapay, pois é de código aberto e sem fins lucrativos. Para mais informações sobre como doar para o NewPipe, visite nosso [site](https://newpipe.net/donate).
From 0e8303f13ae9905e20be9471c8b2cf48a2e04758 Mon Sep 17 00:00:00 2001
From: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
Date: Thu, 25 Jul 2024 14:21:21 +0000
Subject: [PATCH 07/93] Update Matrix room link, and prioritise it (#11350)
* Update Matrix room link, and prioritise it
* Update Matrix room link in CONTRIBUTING.md
* Prioritise Matrix in contribution doc too
---
.github/CONTRIBUTING.md | 6 +++---
.github/ISSUE_TEMPLATE/config.yml | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 70c81c7b1..647cfbabb 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -79,6 +79,6 @@ The [ktlint](https://github.com/pinterest/ktlint) plugin does the same job as ch
## Communication
-* The #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) has the core team and other developers in it. [Click here for webchat](https://web.libera.chat/#newpipe)!
-* You can also use a Matrix account to join the NewPipe channel at [#newpipe:libera.chat](https://matrix.to/#/#newpipe:libera.chat). Some convenient clients, available both for phone and desktop, are listed at that link.
-* You can post your suggestions, changes, ideas etc. on either GitHub or IRC.
+* You can use a Matrix account to join the NewPipe channel at [#newpipe:matrix.newpipe-ev.de](https://matrix.to/#/#newpipe:matrix.newpipe-ev.de). Some convenient clients, available both for phone and desktop, are listed at that link.
+* Alternatively, the #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) can also be joined, as it is bridged to the Matrix room. [Click here for webchat](https://web.libera.chat/#newpipe)!
+* You can post your suggestions, changes, ideas etc. on either GitHub or Matrix (including via IRC).
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 4721637bf..49ab78c7d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,9 +3,9 @@ contact_links:
- name: ❓ Question
url: https://github.com/TeamNewPipe/NewPipe/discussions/new?category=questions
about: Ask about anything NewPipe-related
+ - name: 💬 Matrix
+ url: https://matrix.to/#/#newpipe:matrix.newpipe-ev.de
+ about: Chat with us via Matrix for quick Q/A
- name: 💬 IRC
url: https://web.libera.chat/#newpipe
about: Chat with us via IRC for quick Q/A
- - name: 💬 Matrix
- url: https://matrix.to/#/#newpipe:libera.chat
- about: Chat with us via Matrix for quick Q/A
From 947ac2826a3e118b932a9e781e0ec752a5882e9b Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 25 Jul 2024 18:37:05 +0200
Subject: [PATCH 08/93] Update NewPipeExtractor to v0.24.2
---
app/build.gradle | 2 +-
.../java/org/schabi/newpipe/error/ReCaptchaActivity.java | 9 +++------
.../newpipe/settings/DownloadSettingsFragment.java | 3 +--
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 397a7301e..142a0dba6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -198,7 +198,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.1'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.2'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
diff --git a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
index 3c14cfe4c..42ef261a1 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
@@ -27,8 +27,6 @@ import org.schabi.newpipe.databinding.ActivityRecaptchaBinding;
import org.schabi.newpipe.extractor.utils.Utils;
import org.schabi.newpipe.util.ThemeHelper;
-import java.io.UnsupportedEncodingException;
-
/*
* Created by beneth on 06.12.16.
*
@@ -190,11 +188,10 @@ public class ReCaptchaActivity extends AppCompatActivity {
String abuseCookie = url.substring(abuseStart + 13, abuseEnd);
abuseCookie = Utils.decodeUrlUtf8(abuseCookie);
handleCookies(abuseCookie);
- } catch (UnsupportedEncodingException | StringIndexOutOfBoundsException e) {
+ } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) {
if (MainActivity.DEBUG) {
- e.printStackTrace();
- Log.d(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
- + abuseStart + " and ending at " + abuseEnd + " for url " + url);
+ Log.e(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
+ + abuseStart + " and ending at " + abuseEnd + " for url " + url, e);
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
index 472db6afe..76163b30a 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
@@ -30,7 +30,6 @@ import org.schabi.newpipe.util.FilePickerActivityHelper;
import java.io.File;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
public class DownloadSettingsFragment extends BasePreferenceFragment {
@@ -125,7 +124,7 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
try {
rawUri = decodeUrlUtf8(rawUri);
- } catch (final UnsupportedEncodingException e) {
+ } catch (final IllegalArgumentException e) {
// nothing to do
}
From 56b62413117f3d7ca8a46dc7c326b35f16e512b4 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 25 Jul 2024 18:43:03 +0200
Subject: [PATCH 09/93] Hotfix release v0.27.2 (999)
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 142a0dba6..5706f1c4b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,8 +20,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdk 21
targetSdk 33
- versionCode 998
- versionName "0.27.1"
+ versionCode 999
+ versionName "0.27.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
From 03a6b5c7b9ab1236d42ad2ee7fe953309ee210fd Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 11 Jul 2024 23:39:53 +0200
Subject: [PATCH 10/93] Add changelogs for hotfix release v0.27.2 (999)
---
fastlane/metadata/android/ar/changelogs/999.txt | 1 +
fastlane/metadata/android/az/changelogs/999.txt | 1 +
fastlane/metadata/android/cs/changelogs/999.txt | 1 +
fastlane/metadata/android/de/changelogs/999.txt | 1 +
fastlane/metadata/android/en-US/changelogs/999.txt | 12 ++++++++++++
fastlane/metadata/android/es/changelogs/999.txt | 1 +
fastlane/metadata/android/fa/changelogs/999.txt | 1 +
fastlane/metadata/android/fr/changelogs/999.txt | 1 +
fastlane/metadata/android/he/changelogs/999.txt | 1 +
fastlane/metadata/android/hi/changelogs/999.txt | 1 +
fastlane/metadata/android/id/changelogs/999.txt | 1 +
fastlane/metadata/android/it/changelogs/999.txt | 12 ++++++++++++
fastlane/metadata/android/ka/changelogs/999.txt | 1 +
fastlane/metadata/android/nl/changelogs/999.txt | 1 +
fastlane/metadata/android/pa/changelogs/999.txt | 1 +
fastlane/metadata/android/pt-BR/changelogs/999.txt | 1 +
fastlane/metadata/android/pt-PT/changelogs/999.txt | 1 +
fastlane/metadata/android/pt/changelogs/999.txt | 1 +
fastlane/metadata/android/ru/changelogs/999.txt | 1 +
fastlane/metadata/android/sv/changelogs/999.txt | 1 +
fastlane/metadata/android/tr/changelogs/999.txt | 1 +
fastlane/metadata/android/uk/changelogs/999.txt | 1 +
fastlane/metadata/android/vi/changelogs/999.txt | 1 +
fastlane/metadata/android/zh-Hans/changelogs/999.txt | 1 +
fastlane/metadata/android/zh-Hant/changelogs/999.txt | 1 +
.../metadata/android/zh_Hant_HK/changelogs/999.txt | 1 +
26 files changed, 48 insertions(+)
create mode 100644 fastlane/metadata/android/ar/changelogs/999.txt
create mode 100644 fastlane/metadata/android/az/changelogs/999.txt
create mode 100644 fastlane/metadata/android/cs/changelogs/999.txt
create mode 100644 fastlane/metadata/android/de/changelogs/999.txt
create mode 100644 fastlane/metadata/android/en-US/changelogs/999.txt
create mode 100644 fastlane/metadata/android/es/changelogs/999.txt
create mode 100644 fastlane/metadata/android/fa/changelogs/999.txt
create mode 100644 fastlane/metadata/android/fr/changelogs/999.txt
create mode 100644 fastlane/metadata/android/he/changelogs/999.txt
create mode 100644 fastlane/metadata/android/hi/changelogs/999.txt
create mode 100644 fastlane/metadata/android/id/changelogs/999.txt
create mode 100644 fastlane/metadata/android/it/changelogs/999.txt
create mode 100644 fastlane/metadata/android/ka/changelogs/999.txt
create mode 100644 fastlane/metadata/android/nl/changelogs/999.txt
create mode 100644 fastlane/metadata/android/pa/changelogs/999.txt
create mode 100644 fastlane/metadata/android/pt-BR/changelogs/999.txt
create mode 100644 fastlane/metadata/android/pt-PT/changelogs/999.txt
create mode 100644 fastlane/metadata/android/pt/changelogs/999.txt
create mode 100644 fastlane/metadata/android/ru/changelogs/999.txt
create mode 100644 fastlane/metadata/android/sv/changelogs/999.txt
create mode 100644 fastlane/metadata/android/tr/changelogs/999.txt
create mode 100644 fastlane/metadata/android/uk/changelogs/999.txt
create mode 100644 fastlane/metadata/android/vi/changelogs/999.txt
create mode 100644 fastlane/metadata/android/zh-Hans/changelogs/999.txt
create mode 100644 fastlane/metadata/android/zh-Hant/changelogs/999.txt
create mode 100644 fastlane/metadata/android/zh_Hant_HK/changelogs/999.txt
diff --git a/fastlane/metadata/android/ar/changelogs/999.txt b/fastlane/metadata/android/ar/changelogs/999.txt
new file mode 100644
index 000000000..562f16944
--- /dev/null
+++ b/fastlane/metadata/android/ar/changelogs/999.txt
@@ -0,0 +1 @@
+تم إصلاح YouTube الذي لا يقوم بتشغيل أي دفق
diff --git a/fastlane/metadata/android/az/changelogs/999.txt b/fastlane/metadata/android/az/changelogs/999.txt
new file mode 100644
index 000000000..16a2e1013
--- /dev/null
+++ b/fastlane/metadata/android/az/changelogs/999.txt
@@ -0,0 +1 @@
+YouTube-un heç bir yayım oynatmaması düzəldildi
diff --git a/fastlane/metadata/android/cs/changelogs/999.txt b/fastlane/metadata/android/cs/changelogs/999.txt
new file mode 100644
index 000000000..7035a1112
--- /dev/null
+++ b/fastlane/metadata/android/cs/changelogs/999.txt
@@ -0,0 +1 @@
+Opraveno nepřehrávání jakéhokoli streamu ve službě YouTube
diff --git a/fastlane/metadata/android/de/changelogs/999.txt b/fastlane/metadata/android/de/changelogs/999.txt
new file mode 100644
index 000000000..43623578f
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/999.txt
@@ -0,0 +1 @@
+Behoben, dass YouTube keinen Stream abspielte
diff --git a/fastlane/metadata/android/en-US/changelogs/999.txt b/fastlane/metadata/android/en-US/changelogs/999.txt
new file mode 100644
index 000000000..c089ed197
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/999.txt
@@ -0,0 +1,12 @@
+This hotfix release fixes HTTP 403 errors in the middle of YouTube videos.
+
+New
+• [SoundCloud] Add support for on.soundcloud.com URLs
+
+Improved
+• [Bandcamp] Show additional info in radio kiosk
+
+Fixed
+• [YouTube] Fix occasional HTTP 403 errors at the beginning or in the middle of videos
+• [YouTube] Extract avatar and banner from more channel header types
+• [Bandcamp] Fix various bugs and always use HTTPS
diff --git a/fastlane/metadata/android/es/changelogs/999.txt b/fastlane/metadata/android/es/changelogs/999.txt
new file mode 100644
index 000000000..80b4efa55
--- /dev/null
+++ b/fastlane/metadata/android/es/changelogs/999.txt
@@ -0,0 +1 @@
+Arreglo en YouTube no reproduciendo flujos
diff --git a/fastlane/metadata/android/fa/changelogs/999.txt b/fastlane/metadata/android/fa/changelogs/999.txt
new file mode 100644
index 000000000..ba5413d49
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/999.txt
@@ -0,0 +1 @@
+مشکل عدم نمایش پخشزنده برطرف شد
diff --git a/fastlane/metadata/android/fr/changelogs/999.txt b/fastlane/metadata/android/fr/changelogs/999.txt
new file mode 100644
index 000000000..3ad3bf279
--- /dev/null
+++ b/fastlane/metadata/android/fr/changelogs/999.txt
@@ -0,0 +1 @@
+Correction de YouTube qui ne lisait aucun média
diff --git a/fastlane/metadata/android/he/changelogs/999.txt b/fastlane/metadata/android/he/changelogs/999.txt
new file mode 100644
index 000000000..50731171e
--- /dev/null
+++ b/fastlane/metadata/android/he/changelogs/999.txt
@@ -0,0 +1 @@
+תוקנה התקלה ש־YouTube לא מנגן אף תזרים
diff --git a/fastlane/metadata/android/hi/changelogs/999.txt b/fastlane/metadata/android/hi/changelogs/999.txt
new file mode 100644
index 000000000..071ab64e3
--- /dev/null
+++ b/fastlane/metadata/android/hi/changelogs/999.txt
@@ -0,0 +1 @@
+फिक्स्ड YouTube कोई स्ट्रीम नहीं चला रहा है
diff --git a/fastlane/metadata/android/id/changelogs/999.txt b/fastlane/metadata/android/id/changelogs/999.txt
new file mode 100644
index 000000000..d3fea84ab
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/999.txt
@@ -0,0 +1 @@
+Memperbaiki YouTube yang tidak memutar streaming apa pun
diff --git a/fastlane/metadata/android/it/changelogs/999.txt b/fastlane/metadata/android/it/changelogs/999.txt
new file mode 100644
index 000000000..c2652da28
--- /dev/null
+++ b/fastlane/metadata/android/it/changelogs/999.txt
@@ -0,0 +1,12 @@
+Questo aggiornamento hotfix risolve gli errori HTTP 403 nel mezzo dei video di YouTube.
+
+Novità
+• [SoundCloud] Aggiunto il supporto per gli URL on.soundcloud.com
+
+Migliorie
+• [Bandcamp] Mostra informazioni aggiuntive nel chiosco della radio
+
+Correzioni
+• [YouTube] Corretti errori HTTP 403 occasionali all'inizio o nel mezzo dei video
+• [YouTube] Estrazione avatar e banner da più tipi di canali
+• [Bandcamp] Risolti vari bug e forzato l'uso di HTTPS
diff --git a/fastlane/metadata/android/ka/changelogs/999.txt b/fastlane/metadata/android/ka/changelogs/999.txt
new file mode 100644
index 000000000..d20512f17
--- /dev/null
+++ b/fastlane/metadata/android/ka/changelogs/999.txt
@@ -0,0 +1 @@
+გაასწორა YouTube არ უკრავს არცერთ ნაკადს
diff --git a/fastlane/metadata/android/nl/changelogs/999.txt b/fastlane/metadata/android/nl/changelogs/999.txt
new file mode 100644
index 000000000..9bd8adf86
--- /dev/null
+++ b/fastlane/metadata/android/nl/changelogs/999.txt
@@ -0,0 +1 @@
+YouTube speelt geen stream af opgelost
diff --git a/fastlane/metadata/android/pa/changelogs/999.txt b/fastlane/metadata/android/pa/changelogs/999.txt
new file mode 100644
index 000000000..fe62a1330
--- /dev/null
+++ b/fastlane/metadata/android/pa/changelogs/999.txt
@@ -0,0 +1 @@
+ਸਥਿਰ YouTube ਕੋਈ ਸਟ੍ਰੀਮ ਨਹੀਂ ਚਲਾ ਰਿਹਾ
diff --git a/fastlane/metadata/android/pt-BR/changelogs/999.txt b/fastlane/metadata/android/pt-BR/changelogs/999.txt
new file mode 100644
index 000000000..59fc6a5cd
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/999.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir qualquer transmissão
diff --git a/fastlane/metadata/android/pt-PT/changelogs/999.txt b/fastlane/metadata/android/pt-PT/changelogs/999.txt
new file mode 100644
index 000000000..93519d64d
--- /dev/null
+++ b/fastlane/metadata/android/pt-PT/changelogs/999.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir nenhuma transmissão
diff --git a/fastlane/metadata/android/pt/changelogs/999.txt b/fastlane/metadata/android/pt/changelogs/999.txt
new file mode 100644
index 000000000..93519d64d
--- /dev/null
+++ b/fastlane/metadata/android/pt/changelogs/999.txt
@@ -0,0 +1 @@
+Corrigido YouTube não reproduzir nenhuma transmissão
diff --git a/fastlane/metadata/android/ru/changelogs/999.txt b/fastlane/metadata/android/ru/changelogs/999.txt
new file mode 100644
index 000000000..d3978869d
--- /dev/null
+++ b/fastlane/metadata/android/ru/changelogs/999.txt
@@ -0,0 +1 @@
+Исправлено: YouTube не воспроизводил никакие потоки
diff --git a/fastlane/metadata/android/sv/changelogs/999.txt b/fastlane/metadata/android/sv/changelogs/999.txt
new file mode 100644
index 000000000..35f298dbf
--- /dev/null
+++ b/fastlane/metadata/android/sv/changelogs/999.txt
@@ -0,0 +1 @@
+Åtgärdat att YouTube inte spelar någon stream
diff --git a/fastlane/metadata/android/tr/changelogs/999.txt b/fastlane/metadata/android/tr/changelogs/999.txt
new file mode 100644
index 000000000..e5979c68d
--- /dev/null
+++ b/fastlane/metadata/android/tr/changelogs/999.txt
@@ -0,0 +1 @@
+YouTube'un herhangi bir akışı oynatmaması düzeltildi
diff --git a/fastlane/metadata/android/uk/changelogs/999.txt b/fastlane/metadata/android/uk/changelogs/999.txt
new file mode 100644
index 000000000..905287c74
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/999.txt
@@ -0,0 +1 @@
+Виправлено проблему невідтворюваності трансляцій
diff --git a/fastlane/metadata/android/vi/changelogs/999.txt b/fastlane/metadata/android/vi/changelogs/999.txt
new file mode 100644
index 000000000..d2086b62c
--- /dev/null
+++ b/fastlane/metadata/android/vi/changelogs/999.txt
@@ -0,0 +1 @@
+Đã sửa lỗi YouTube không phát bất kỳ luồng nào
diff --git a/fastlane/metadata/android/zh-Hans/changelogs/999.txt b/fastlane/metadata/android/zh-Hans/changelogs/999.txt
new file mode 100644
index 000000000..8a5424c9e
--- /dev/null
+++ b/fastlane/metadata/android/zh-Hans/changelogs/999.txt
@@ -0,0 +1 @@
+修复YouTube无法播放任何视频
diff --git a/fastlane/metadata/android/zh-Hant/changelogs/999.txt b/fastlane/metadata/android/zh-Hant/changelogs/999.txt
new file mode 100644
index 000000000..4e8bf6537
--- /dev/null
+++ b/fastlane/metadata/android/zh-Hant/changelogs/999.txt
@@ -0,0 +1 @@
+修正 YouTube 無法播放任何串流
diff --git a/fastlane/metadata/android/zh_Hant_HK/changelogs/999.txt b/fastlane/metadata/android/zh_Hant_HK/changelogs/999.txt
new file mode 100644
index 000000000..9a4721551
--- /dev/null
+++ b/fastlane/metadata/android/zh_Hant_HK/changelogs/999.txt
@@ -0,0 +1 @@
+修正咗 YouTube 乜嘢實況串流都播唔到嘅問題
From dbcb721dc21429c25aa164afb98dc3b91e19672b Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 25 Jul 2024 20:56:16 +0200
Subject: [PATCH 11/93] Don't warn about rhino class in proguard
Likely related to https://github.com/mozilla/rhino/commit/01a7b20655602f7e2df59af744b47b77f678b6cf but I am not completely sure.
I tested the app and it works well, so I think that org.mozilla.javascript.JavaToJSONConverters is not used really.
This is the full list of errors:
Missing class java.beans.BeanDescriptor (referenced from: java.lang.Object org.mozilla.javascript.JavaToJSONConverters.lambda$static$4(java.lang.Object))
Missing class java.beans.BeanInfo (referenced from: java.lang.Object org.mozilla.javascript.JavaToJSONConverters.lambda$static$4(java.lang.Object))
Missing class java.beans.IntrospectionException (referenced from: java.lang.Object org.mozilla.javascript.JavaToJSONConverters.lambda$static$4(java.lang.Object))
Missing class java.beans.Introspector (referenced from: java.lang.Object org.mozilla.javascript.JavaToJSONConverters.lambda$static$4(java.lang.Object))
Missing class java.beans.PropertyDescriptor (referenced from: java.lang.Object org.mozilla.javascript.JavaToJSONConverters.lambda$static$4(java.lang.Object))
---
app/build.gradle | 3 +++
app/proguard-rules.pro | 1 +
2 files changed, 4 insertions(+)
diff --git a/app/build.gradle b/app/build.gradle
index 5706f1c4b..35cb8509e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -56,6 +56,9 @@ android {
resValue "string", "app_name", "NewPipe " + System.getProperty('packageSuffix')
archivesBaseName = 'NewPipe_' + System.getProperty('packageSuffix')
}
+ applicationIdSuffix ".whatever"
+ resValue "string", "app_name", "NewPipe whatever"
+ archivesBaseName = 'NewPipe_whatever'
minifyEnabled true
shrinkResources false // disabled to fix F-Droid's reproducible build
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index d21f33e1f..435c4e29b 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -7,6 +7,7 @@
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter
+-dontwarn org.mozilla.javascript.JavaToJSONConverters
-dontwarn org.mozilla.javascript.tools.**
## Rules for ExoPlayer
From d442b458361b002ab90d9608db223203601ee2cc Mon Sep 17 00:00:00 2001
From: Stypox
Date: Thu, 25 Jul 2024 20:58:29 +0200
Subject: [PATCH 12/93] Remove code committed accidentally
---
app/build.gradle | 3 ---
1 file changed, 3 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 35cb8509e..5706f1c4b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -56,9 +56,6 @@ android {
resValue "string", "app_name", "NewPipe " + System.getProperty('packageSuffix')
archivesBaseName = 'NewPipe_' + System.getProperty('packageSuffix')
}
- applicationIdSuffix ".whatever"
- resValue "string", "app_name", "NewPipe whatever"
- archivesBaseName = 'NewPipe_whatever'
minifyEnabled true
shrinkResources false // disabled to fix F-Droid's reproducible build
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
From bda961a04cb8172bd3d5d0c4d7ea61da965e8c72 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 12 May 2024 08:21:13 +0530
Subject: [PATCH 13/93] Convert comment replies views to Jetpack Compose
---
app/build.gradle | 6 +-
.../fragments/list/comments/Comment.kt | 84 ++++++++++++
.../list/comments/CommentRepliesHeader.kt | 121 ++++++++++++++++++
build.gradle | 2 +-
4 files changed, 209 insertions(+), 4 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
diff --git a/app/build.gradle b/app/build.gradle
index 9ea725ad9..5b5b25350 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -106,7 +106,7 @@ android {
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.5.3"
+ kotlinCompilerExtensionVersion = "1.5.13"
}
}
@@ -267,7 +267,7 @@ dependencies {
implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}"
// Image loading
- implementation 'io.coil-kt:coil:2.7.0'
+ implementation 'io.coil-kt:coil-compose:2.7.0'
// Markdown library for Android
implementation "io.noties.markwon:core:${markwonVersion}"
@@ -289,7 +289,7 @@ dependencies {
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
// Jetpack Compose
- implementation(platform('androidx.compose:compose-bom:2024.02.01'))
+ implementation(platform('androidx.compose:compose-bom:2024.05.00'))
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
new file mode 100644
index 000000000..c622aa18d
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -0,0 +1,84 @@
+package org.schabi.newpipe.fragments.list.comments
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.FragmentActivity
+import coil.compose.AsyncImage
+import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.util.NavigationHelper
+import org.schabi.newpipe.util.image.ImageStrategy
+
+@Composable
+fun Comment(comment: CommentsInfoItem) {
+ val context = LocalContext.current
+
+ Row(modifier = Modifier.padding(all = 8.dp)) {
+ if (ImageStrategy.shouldLoadImages()) {
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(context as FragmentActivity, comment)
+ }
+ )
+ }
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
+
+ Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
+ Text(
+ text = comment.uploaderName,
+ color = MaterialTheme.colors.secondary
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Text(
+ text = comment.commentText.content,
+ // If the comment is expanded, we display all its content
+ // otherwise we only display the first line
+ maxLines = if (isExpanded) Int.MAX_VALUE else 1,
+ style = MaterialTheme.typography.body2
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun CommentPreview() {
+ val comment = CommentsInfoItem(1, "", "")
+ comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
+ comment.uploaderName = "Test"
+
+ Comment(comment)
+}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
new file mode 100644
index 000000000..243d887cd
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -0,0 +1,121 @@
+package org.schabi.newpipe.fragments.list.comments
+
+import android.widget.TextView
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.text.HtmlCompat
+import androidx.core.text.method.LinkMovementMethodCompat
+import androidx.fragment.app.FragmentActivity
+import coil.compose.AsyncImage
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.util.Localization
+import org.schabi.newpipe.util.NavigationHelper
+import org.schabi.newpipe.util.ServiceHelper
+import org.schabi.newpipe.util.image.ImageStrategy
+import org.schabi.newpipe.util.text.TextLinkifier
+
+@Composable
+fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) {
+ val context = LocalContext.current
+
+ Column(modifier = Modifier.padding(all = 8.dp)) {
+ Row {
+ Row(
+ modifier = Modifier
+ .padding(all = 8.dp)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(
+ context as FragmentActivity,
+ comment
+ )
+ }
+ ) {
+ if (ImageStrategy.shouldLoadImages()) {
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ )
+ }
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ Column {
+ Text(text = comment.uploaderName)
+
+ Text(
+ text = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
+ )
+ }
+ }
+
+ if (comment.isHeartedByUploader) {
+ Image(
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ )
+ }
+
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
+ }
+ }
+
+ AndroidView(
+ factory = { context ->
+ TextView(context).apply {
+ movementMethod = LinkMovementMethodCompat.getInstance()
+ }
+ },
+ update = { view ->
+ // setup comment content
+ TextLinkifier.fromDescription(view, comment.commentText,
+ HtmlCompat.FROM_HTML_MODE_LEGACY,
+ ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
+ null
+ )
+ }
+ )
+ }
+}
+
+@Preview
+@Composable
+fun CommentRepliesHeaderPreview() {
+ val disposables = CompositeDisposable()
+ val comment = CommentsInfoItem(1, "", "")
+ comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
+ comment.uploaderName = "Test"
+ comment.textualUploadDate = "5 months ago"
+
+ CommentRepliesHeader(comment, disposables)
+}
diff --git a/build.gradle b/build.gradle
index 6d19a6f8a..5a1ae1945 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.9.10'
+ ext.kotlin_version = '1.9.23'
repositories {
google()
mavenCentral()
From 644a345b5571fd34ae3471c686ee59afc98fe412 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 12 May 2024 08:49:14 +0530
Subject: [PATCH 14/93] Rename .java to .kt
---
.../{CommentRepliesFragment.java => CommentRepliesFragment.kt} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename app/src/main/java/org/schabi/newpipe/fragments/list/comments/{CommentRepliesFragment.java => CommentRepliesFragment.kt} (100%)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
similarity index 100%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java
rename to app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
From e05d97732eb48c1641367234a26f98db94c1bb98 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 12 May 2024 08:49:15 +0530
Subject: [PATCH 15/93] Use reply header composable in fragment
---
.../java/org/schabi/newpipe/MainActivity.java | 2 +-
.../list/comments/CommentRepliesFragment.kt | 260 +++++++-----------
.../list/comments/CommentRepliesHeader.kt | 4 +-
.../res/layout/comment_replies_header.xml | 137 ---------
4 files changed, 100 insertions(+), 303 deletions(-)
delete mode 100644 app/src/main/res/layout/comment_replies_header.xml
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 175694125..43b01c70d 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -871,7 +871,7 @@ public class MainActivity extends AppCompatActivity {
@Nullable final CommentRepliesFragment repliesFragment =
(CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG);
@Nullable final CommentsInfoItem rootComment =
- repliesFragment == null ? null : repliesFragment.getCommentsInfoItem();
+ repliesFragment == null ? null : repliesFragment.commentsInfoItem;
// sometimes this function pops the backstack, other times it's handled by the system
if (popBackStack) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 4eb73520f..4bfb04d6e 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -1,170 +1,104 @@
-package org.schabi.newpipe.fragments.list.comments;
+package org.schabi.newpipe.fragments.list.comments
-import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
-
-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.constraintlayout.widget.ConstraintLayout;
-import androidx.core.text.HtmlCompat;
-
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.databinding.CommentRepliesHeaderBinding;
-import org.schabi.newpipe.error.UserAction;
-import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
-import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
-import org.schabi.newpipe.info_list.ItemViewMode;
-import org.schabi.newpipe.util.DeviceUtils;
-import org.schabi.newpipe.util.ExtractorHelper;
-import org.schabi.newpipe.util.Localization;
-import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.image.CoilHelper;
-import org.schabi.newpipe.util.image.ImageStrategy;
-import org.schabi.newpipe.util.text.TextLinkifier;
-
-import java.util.Queue;
-import java.util.function.Supplier;
-
-import icepick.State;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public final class CommentRepliesFragment
- extends BaseListInfoFragment {
-
- public static final String TAG = CommentRepliesFragment.class.getSimpleName();
-
- @State
- CommentsInfoItem commentsInfoItem; // the comment to show replies of
- private final CompositeDisposable disposables = new CompositeDisposable();
-
-
- /*//////////////////////////////////////////////////////////////////////////
- // Constructors and lifecycle
- //////////////////////////////////////////////////////////////////////////*/
-
- // only called by the Android framework, after which readFrom is called and restores all data
- public CommentRepliesFragment() {
- super(UserAction.REQUESTED_COMMENT_REPLIES);
- }
-
- public CommentRepliesFragment(@NonNull final CommentsInfoItem commentsInfoItem) {
- this();
- this.commentsInfoItem = commentsInfoItem;
- // setting "" as title since the title will be properly set right after
- setInitialData(commentsInfoItem.getServiceId(), commentsInfoItem.getUrl(), "");
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull final LayoutInflater inflater,
- @Nullable final ViewGroup container,
- @Nullable final Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_comments, container, false);
- }
-
- @Override
- public void onDestroyView() {
- disposables.clear();
- super.onDestroyView();
- }
-
- @Override
- protected Supplier getListHeaderSupplier() {
- return () -> {
- final CommentRepliesHeaderBinding binding = CommentRepliesHeaderBinding
- .inflate(activity.getLayoutInflater(), itemsList, false);
- final CommentsInfoItem item = commentsInfoItem;
-
- // load the author avatar
- CoilHelper.INSTANCE.loadAvatar(binding.authorAvatar, item.getUploaderAvatars());
- binding.authorAvatar.setVisibility(ImageStrategy.shouldLoadImages()
- ? View.VISIBLE : View.GONE);
-
- // setup author name and comment date
- binding.authorName.setText(item.getUploaderName());
- binding.uploadDate.setText(Localization.relativeTimeOrTextual(
- getContext(), item.getUploadDate(), item.getTextualUploadDate()));
- binding.authorTouchArea.setOnClickListener(
- v -> NavigationHelper.openCommentAuthorIfPresent(requireActivity(), item));
-
- // setup like count, hearted and pinned
- binding.thumbsUpCount.setText(
- Localization.likeCount(requireContext(), item.getLikeCount()));
- // for heartImage goneMarginEnd was used, but there is no way to tell ConstraintLayout
- // not to use a different margin only when both the next two views are gone
- ((ConstraintLayout.LayoutParams) binding.thumbsUpCount.getLayoutParams())
- .setMarginEnd(DeviceUtils.dpToPx(
- (item.isHeartedByUploader() || item.isPinned() ? 8 : 16),
- requireContext()));
- binding.heartImage.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE);
- binding.pinnedImage.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE);
-
- // setup comment content
- TextLinkifier.fromDescription(binding.commentContent, item.getCommentText(),
- HtmlCompat.FROM_HTML_MODE_LEGACY, getServiceById(item.getServiceId()),
- item.getUrl(), disposables, null);
-
- return binding.getRoot();
- };
- }
-
-
- /*//////////////////////////////////////////////////////////////////////////
- // State saving
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void writeTo(final Queue objectsToSave) {
- super.writeTo(objectsToSave);
- objectsToSave.add(commentsInfoItem);
- }
-
- @Override
- public void readFrom(@NonNull final Queue savedObjects) throws Exception {
- super.readFrom(savedObjects);
- commentsInfoItem = (CommentsInfoItem) savedObjects.poll();
- }
-
-
- /*//////////////////////////////////////////////////////////////////////////
- // Data loading
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- protected Single loadResult(final boolean forceLoad) {
- return Single.fromCallable(() -> new CommentRepliesInfo(commentsInfoItem,
- // the reply count string will be shown as the activity title
- Localization.replyCount(requireContext(), commentsInfoItem.getReplyCount())));
- }
-
- @Override
- protected Single> loadMoreItemsLogic() {
- // commentsInfoItem.getUrl() should contain the url of the original
- // ListInfo, which should be the stream url
- return ExtractorHelper.getMoreCommentItems(
- serviceId, commentsInfoItem.getUrl(), currentNextPage);
- }
-
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- protected ItemViewMode getItemViewMode() {
- return ItemViewMode.LIST;
- }
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import icepick.State
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import org.schabi.newpipe.R
+import org.schabi.newpipe.error.UserAction
+import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.fragments.list.BaseListInfoFragment
+import org.schabi.newpipe.info_list.ItemViewMode
+import org.schabi.newpipe.util.ExtractorHelper
+import org.schabi.newpipe.util.Localization
+import java.util.Queue
+import java.util.function.Supplier
+class CommentRepliesFragment() : BaseListInfoFragment(UserAction.REQUESTED_COMMENT_REPLIES) {
/**
* @return the comment to which the replies are shown
*/
- public CommentsInfoItem getCommentsInfoItem() {
- return commentsInfoItem;
+ @State
+ lateinit var commentsInfoItem: CommentsInfoItem // the comment to show replies of
+ private val disposables = CompositeDisposable()
+
+ constructor(commentsInfoItem: CommentsInfoItem) : this() {
+ this.commentsInfoItem = commentsInfoItem
+ // setting "" as title since the title will be properly set right after
+ setInitialData(commentsInfoItem.serviceId, commentsInfoItem.url, "")
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ return inflater.inflate(R.layout.fragment_comments, container, false)
+ }
+
+ override fun onDestroyView() {
+ disposables.clear()
+ super.onDestroyView()
+ }
+
+ override fun getListHeaderSupplier(): Supplier {
+ return Supplier {
+ ComposeView(requireContext()).apply {
+ setContent {
+ CommentRepliesHeader(commentsInfoItem, disposables)
+ }
+ }
+ }
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // State saving
+ ////////////////////////////////////////////////////////////////////////// */
+ override fun writeTo(objectsToSave: Queue) {
+ super.writeTo(objectsToSave)
+ objectsToSave.add(commentsInfoItem)
+ }
+
+ @Throws(Exception::class)
+ override fun readFrom(savedObjects: Queue) {
+ super.readFrom(savedObjects)
+ commentsInfoItem = savedObjects.poll() as CommentsInfoItem
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Data loading
+ ////////////////////////////////////////////////////////////////////////// */
+ override fun loadResult(forceLoad: Boolean): Single {
+ return Single.fromCallable {
+ CommentRepliesInfo(
+ commentsInfoItem, // the reply count string will be shown as the activity title
+ Localization.replyCount(requireContext(), commentsInfoItem.replyCount)
+ )
+ }
+ }
+
+ override fun loadMoreItemsLogic(): Single>? {
+ // commentsInfoItem.getUrl() should contain the url of the original
+ // ListInfo, which should be the stream url
+ return ExtractorHelper.getMoreCommentItems(
+ serviceId, commentsInfoItem.url, currentNextPage
+ )
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Utils
+ ////////////////////////////////////////////////////////////////////////// */
+ override fun getItemViewMode(): ItemViewMode {
+ return ItemViewMode.LIST
+ }
+
+ companion object {
+ @JvmField
+ val TAG: String = CommentRepliesFragment::class.java.simpleName
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 243d887cd..89f6985a3 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -98,8 +98,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
},
update = { view ->
// setup comment content
- TextLinkifier.fromDescription(view, comment.commentText,
- HtmlCompat.FROM_HTML_MODE_LEGACY,
+ TextLinkifier.fromDescription(
+ view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
null
)
diff --git a/app/src/main/res/layout/comment_replies_header.xml b/app/src/main/res/layout/comment_replies_header.xml
deleted file mode 100644
index ed5ba1a10..000000000
--- a/app/src/main/res/layout/comment_replies_header.xml
+++ /dev/null
@@ -1,137 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From 8ce9a7e43c1bb4aa9213d56725aa3eaaf6fbc96d Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 12 May 2024 18:09:16 +0530
Subject: [PATCH 16/93] Added like count
---
.../list/comments/CommentRepliesHeader.kt | 47 ++++++++++++++-----
1 file changed, 35 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 89f6985a3..cbd0a8f1b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -3,15 +3,18 @@ package org.schabi.newpipe.fragments.list.comments
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
@@ -39,16 +42,20 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
val context = LocalContext.current
Column(modifier = Modifier.padding(all = 8.dp)) {
- Row {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
Row(
modifier = Modifier
- .padding(all = 8.dp)
+ .padding(top = 8.dp, bottom = 8.dp, end = 8.dp)
.clickable {
NavigationHelper.openCommentAuthorIfPresent(
context as FragmentActivity,
comment
)
- }
+ },
+ verticalAlignment = Alignment.CenterVertically
) {
if (ImageStrategy.shouldLoadImages()) {
AsyncImage(
@@ -75,18 +82,31 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
}
}
- if (comment.isHeartedByUploader) {
- Image(
- painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
- )
- }
+ Spacer(modifier = Modifier.weight(1f))
- if (comment.isPinned) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
)
+ Text(text = comment.likeCount.toString())
+
+ if (comment.isHeartedByUploader) {
+ Image(
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ )
+ }
+
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
+ }
}
}
@@ -116,6 +136,9 @@ fun CommentRepliesHeaderPreview() {
comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
comment.uploaderName = "Test"
comment.textualUploadDate = "5 months ago"
+ comment.likeCount = 100
+ comment.isPinned = true
+ comment.isHeartedByUploader = true
CommentRepliesHeader(comment, disposables)
}
From 56c80ce6dd554eab6899a05066c8274b34e6f8d2 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 17 May 2024 10:17:09 +0530
Subject: [PATCH 17/93] Added missing comment features, fixed theming
---
.../java/org/schabi/newpipe/MainActivity.java | 2 +-
.../fragments/list/comments/Comment.kt | 123 +++++++++----
.../list/comments/CommentRepliesFragment.kt | 5 +-
.../list/comments/CommentRepliesHeader.kt | 165 ++++++++++--------
4 files changed, 181 insertions(+), 114 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 43b01c70d..175694125 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -871,7 +871,7 @@ public class MainActivity extends AppCompatActivity {
@Nullable final CommentRepliesFragment repliesFragment =
(CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG);
@Nullable final CommentsInfoItem rootComment =
- repliesFragment == null ? null : repliesFragment.commentsInfoItem;
+ repliesFragment == null ? null : repliesFragment.getCommentsInfoItem();
// sometimes this function pops the backstack, other times it's handled by the system
if (popBackStack) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index c622aa18d..b293adedc 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -1,16 +1,19 @@
package org.schabi.newpipe.fragments.list.comments
+import android.content.res.Configuration
+import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Text
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -20,6 +23,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
@@ -27,6 +31,7 @@ import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
@@ -34,51 +39,93 @@ import org.schabi.newpipe.util.image.ImageStrategy
fun Comment(comment: CommentsInfoItem) {
val context = LocalContext.current
- Row(modifier = Modifier.padding(all = 8.dp)) {
- if (ImageStrategy.shouldLoadImages()) {
- AsyncImage(
- model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
- contentDescription = null,
- placeholder = painterResource(R.drawable.placeholder_person),
- error = painterResource(R.drawable.placeholder_person),
- modifier = Modifier
- .size(42.dp)
- .clip(CircleShape)
- .clickable {
- NavigationHelper.openCommentAuthorIfPresent(context as FragmentActivity, comment)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ Row(modifier = Modifier.padding(all = 8.dp)) {
+ if (ImageStrategy.shouldLoadImages()) {
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(
+ context as FragmentActivity,
+ comment
+ )
+ }
+ )
+ }
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
+
+ Column(
+ modifier = Modifier.clickable { isExpanded = !isExpanded },
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = comment.uploaderName,
+ color = MaterialTheme.colorScheme.secondary
+ )
+
+ Text(
+ text = comment.commentText.content,
+ // If the comment is expanded, we display all its content
+ // otherwise we only display the first two lines
+ maxLines = if (isExpanded) Int.MAX_VALUE else 2,
+ style = MaterialTheme.typography.bodyMedium
+ )
+
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Image(
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ )
+ Text(text = comment.likeCount.toString())
+
+ if (comment.isHeartedByUploader) {
+ Image(
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ )
}
- )
+
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
+ }
+ }
+ }
}
- Spacer(modifier = Modifier.width(8.dp))
-
- var isExpanded by rememberSaveable { mutableStateOf(false) }
-
- Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
- Text(
- text = comment.uploaderName,
- color = MaterialTheme.colors.secondary
- )
-
- Spacer(modifier = Modifier.height(4.dp))
-
- Text(
- text = comment.commentText.content,
- // If the comment is expanded, we display all its content
- // otherwise we only display the first line
- maxLines = if (isExpanded) Int.MAX_VALUE else 1,
- style = MaterialTheme.typography.body2
- )
- }
+ // TODO: Add support for comment replies
}
}
-@Preview
+@Preview(
+ name = "Light mode",
+ uiMode = Configuration.UI_MODE_NIGHT_NO
+)
+@Preview(
+ name = "Dark mode",
+ uiMode = Configuration.UI_MODE_NIGHT_YES
+)
@Composable
fun CommentPreview() {
val comment = CommentsInfoItem(1, "", "")
comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
comment.uploaderName = "Test"
+ comment.likeCount = 100
+ comment.isHeartedByUploader = true
+ comment.isPinned = true
- Comment(comment)
+ AppTheme {
+ Comment(comment)
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 4bfb04d6e..1847d7d31 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -14,6 +14,7 @@ import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.fragments.list.BaseListInfoFragment
import org.schabi.newpipe.info_list.ItemViewMode
+import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.ExtractorHelper
import org.schabi.newpipe.util.Localization
import java.util.Queue
@@ -50,7 +51,9 @@ class CommentRepliesFragment() : BaseListInfoFragment
+ TextView(context).apply {
+ movementMethod = LinkMovementMethodCompat.getInstance()
+ }
+ },
+ update = { view ->
+ // setup comment content
+ TextLinkifier.fromDescription(
+ view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
+ ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
+ null
)
}
-
- if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
- )
- }
- }
+ )
}
-
- AndroidView(
- factory = { context ->
- TextView(context).apply {
- movementMethod = LinkMovementMethodCompat.getInstance()
- }
- },
- update = { view ->
- // setup comment content
- TextLinkifier.fromDescription(
- view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
- ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
- null
- )
- }
- )
}
}
-@Preview
+@Preview(
+ name = "Light mode",
+ uiMode = Configuration.UI_MODE_NIGHT_NO
+)
+@Preview(
+ name = "Dark mode",
+ uiMode = Configuration.UI_MODE_NIGHT_YES
+)
@Composable
fun CommentRepliesHeaderPreview() {
val disposables = CompositeDisposable()
@@ -140,5 +155,7 @@ fun CommentRepliesHeaderPreview() {
comment.isPinned = true
comment.isHeartedByUploader = true
- CommentRepliesHeader(comment, disposables)
+ AppTheme {
+ CommentRepliesHeader(comment, disposables)
+ }
}
From 1620668966cc9793cec97ab68149bbc7d5120874 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 16 Jun 2024 18:29:29 +0530
Subject: [PATCH 18/93] Add comment ellipsis
---
.../newpipe/fragments/list/comments/Comment.kt | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index b293adedc..8b1726fff 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -24,6 +24,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
@@ -77,6 +78,7 @@ fun Comment(comment: CommentsInfoItem) {
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
+ overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium
)
@@ -108,18 +110,12 @@ fun Comment(comment: CommentsInfoItem) {
}
}
-@Preview(
- name = "Light mode",
- uiMode = Configuration.UI_MODE_NIGHT_NO
-)
-@Preview(
- name = "Dark mode",
- uiMode = Configuration.UI_MODE_NIGHT_YES
-)
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CommentPreview() {
val comment = CommentsInfoItem(1, "", "")
- comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
+ comment.commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT)
comment.uploaderName = "Test"
comment.likeCount = 100
comment.isHeartedByUploader = true
From 341cc37ce76933bdaa8ae45b7ef17c2828324d83 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 18 Jun 2024 07:59:24 +0530
Subject: [PATCH 19/93] Update replies fragment to use the comment composable
as well
---
app/build.gradle | 8 +-
.../java/org/schabi/newpipe/MainActivity.java | 8 +-
.../fragments/list/comments/Comment.kt | 32 ++++--
.../fragments/list/comments/CommentReplies.kt | 60 +++++++++++
.../list/comments/CommentRepliesFragment.kt | 100 ++++--------------
.../list/comments/CommentRepliesHeader.kt | 10 +-
.../list/comments/CommentRepliesInfo.java | 22 ----
.../list/comments/CommentRepliesSource.kt | 22 ++++
.../java/org/schabi/newpipe/ktx/Bundle.kt | 16 +++
.../schabi/newpipe/util/NavigationHelper.java | 6 +-
build.gradle | 2 +-
11 files changed, 163 insertions(+), 123 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
delete mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
diff --git a/app/build.gradle b/app/build.gradle
index 5b5b25350..200af2209 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -106,7 +106,7 @@ android {
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.5.13"
+ kotlinCompilerExtensionVersion = "1.5.14"
}
}
@@ -289,11 +289,15 @@ dependencies {
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
// Jetpack Compose
- implementation(platform('androidx.compose:compose-bom:2024.05.00'))
+ implementation(platform('androidx.compose:compose-bom:2024.06.00'))
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
+ // Paging
+ implementation 'androidx.paging:paging-rxjava3:3.3.0'
+ implementation 'androidx.paging:paging-compose:3.3.0'
+
/** Debugging **/
// Memory leak detection
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 175694125..d288ab4c6 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -66,7 +66,6 @@ import org.schabi.newpipe.databinding.ToolbarLayoutBinding;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
import org.schabi.newpipe.fragments.BackPressable;
@@ -868,10 +867,9 @@ public class MainActivity extends AppCompatActivity {
}
// the root comment is the comment for which the user opened the replies page
- @Nullable final CommentRepliesFragment repliesFragment =
- (CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG);
- @Nullable final CommentsInfoItem rootComment =
- repliesFragment == null ? null : repliesFragment.getCommentsInfoItem();
+ final var repliesFragment = (CommentRepliesFragment)
+ fm.findFragmentByTag(CommentRepliesFragment.TAG);
+ final var rootComment = repliesFragment == null ? null : repliesFragment.getComment();
// sometimes this function pops the backstack, other times it's handled by the system
if (popBackStack) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index 8b1726fff..bdffa8561 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -73,6 +73,7 @@ fun Comment(comment: CommentsInfoItem) {
color = MaterialTheme.colorScheme.secondary
)
+ // TODO: Handle HTML and Markdown formats.
Text(
text = comment.commentText.content,
// If the comment is expanded, we display all its content
@@ -110,16 +111,33 @@ fun Comment(comment: CommentsInfoItem) {
}
}
+fun CommentsInfoItem(
+ serviceId: Int = 1,
+ url: String = "",
+ name: String = "",
+ commentText: Description,
+ uploaderName: String,
+ textualUploadDate: String = "5 months ago",
+ likeCount: Int = 100,
+ isHeartedByUploader: Boolean = true,
+ isPinned: Boolean = true,
+) = CommentsInfoItem(serviceId, url, name).apply {
+ this.commentText = commentText
+ this.uploaderName = uploaderName
+ this.textualUploadDate = textualUploadDate
+ this.likeCount = likeCount
+ this.isHeartedByUploader = isHeartedByUploader
+ this.isPinned = isPinned
+}
+
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-fun CommentPreview() {
- val comment = CommentsInfoItem(1, "", "")
- comment.commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT)
- comment.uploaderName = "Test"
- comment.likeCount = 100
- comment.isHeartedByUploader = true
- comment.isPinned = true
+private fun CommentPreview() {
+ val comment = CommentsInfoItem(
+ commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ )
AppTheme {
Comment(comment)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
new file mode 100644
index 000000000..05a6d1c3b
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
@@ -0,0 +1,60 @@
+package org.schabi.newpipe.fragments.list.comments
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.paging.PagingData
+import androidx.paging.compose.collectAsLazyPagingItems
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.ui.theme.AppTheme
+
+@Composable
+fun CommentReplies(
+ comment: CommentsInfoItem,
+ flow: Flow>,
+ disposables: CompositeDisposable
+) {
+ val replies = flow.collectAsLazyPagingItems()
+
+ Column {
+ CommentRepliesHeader(comment = comment, disposables = disposables)
+ HorizontalDivider(thickness = 1.dp)
+ LazyColumn {
+ items(replies.itemCount) {
+ Comment(comment = replies[it]!!)
+ }
+ }
+ }
+}
+
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CommentRepliesPreview() {
+ val comment = CommentsInfoItem(
+ commentText = Description("Hello world!", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ )
+
+ val reply1 = CommentsInfoItem(
+ commentText = Description("This is a reply", Description.PLAIN_TEXT),
+ uploaderName = "Test 2",
+ )
+ val reply2 = CommentsInfoItem(
+ commentText = Description("This is another reply. This is another line.", Description.HTML),
+ uploaderName = "Test 3",
+ )
+ val flow = flowOf(PagingData.from(listOf(reply1, reply2)))
+
+ AppTheme {
+ CommentReplies(comment = comment, flow = flow, disposables = CompositeDisposable())
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 1847d7d31..6afbc7e20 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -4,104 +4,50 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
-import icepick.State
-import io.reactivex.rxjava3.core.Single
+import androidx.fragment.app.Fragment
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
import io.reactivex.rxjava3.disposables.CompositeDisposable
-import org.schabi.newpipe.R
-import org.schabi.newpipe.error.UserAction
-import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
-import org.schabi.newpipe.fragments.list.BaseListInfoFragment
-import org.schabi.newpipe.info_list.ItemViewMode
+import org.schabi.newpipe.ktx.serializable
import org.schabi.newpipe.ui.theme.AppTheme
-import org.schabi.newpipe.util.ExtractorHelper
-import org.schabi.newpipe.util.Localization
-import java.util.Queue
-import java.util.function.Supplier
-class CommentRepliesFragment() : BaseListInfoFragment(UserAction.REQUESTED_COMMENT_REPLIES) {
- /**
- * @return the comment to which the replies are shown
- */
- @State
- lateinit var commentsInfoItem: CommentsInfoItem // the comment to show replies of
+class CommentRepliesFragment : Fragment() {
private val disposables = CompositeDisposable()
-
- constructor(commentsInfoItem: CommentsInfoItem) : this() {
- this.commentsInfoItem = commentsInfoItem
- // setting "" as title since the title will be properly set right after
- setInitialData(commentsInfoItem.serviceId, commentsInfoItem.url, "")
- }
+ lateinit var comment: CommentsInfoItem
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
- return inflater.inflate(R.layout.fragment_comments, container, false)
- }
+ comment = requireArguments().serializable(COMMENT_KEY)!!
+ return ComposeView(requireContext()).apply {
+ setContent {
+ val flow = remember(comment) {
+ Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentRepliesSource(comment)
+ }.flow
+ }
- override fun onDestroyView() {
- disposables.clear()
- super.onDestroyView()
- }
-
- override fun getListHeaderSupplier(): Supplier {
- return Supplier {
- ComposeView(requireContext()).apply {
- setContent {
- AppTheme {
- CommentRepliesHeader(commentsInfoItem, disposables)
- }
+ AppTheme {
+ CommentReplies(comment = comment, flow = flow, disposables = disposables)
}
}
}
}
- /*//////////////////////////////////////////////////////////////////////////
- // State saving
- ////////////////////////////////////////////////////////////////////////// */
- override fun writeTo(objectsToSave: Queue) {
- super.writeTo(objectsToSave)
- objectsToSave.add(commentsInfoItem)
- }
-
- @Throws(Exception::class)
- override fun readFrom(savedObjects: Queue) {
- super.readFrom(savedObjects)
- commentsInfoItem = savedObjects.poll() as CommentsInfoItem
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Data loading
- ////////////////////////////////////////////////////////////////////////// */
- override fun loadResult(forceLoad: Boolean): Single {
- return Single.fromCallable {
- CommentRepliesInfo(
- commentsInfoItem, // the reply count string will be shown as the activity title
- Localization.replyCount(requireContext(), commentsInfoItem.replyCount)
- )
- }
- }
-
- override fun loadMoreItemsLogic(): Single>? {
- // commentsInfoItem.getUrl() should contain the url of the original
- // ListInfo, which should be the stream url
- return ExtractorHelper.getMoreCommentItems(
- serviceId, commentsInfoItem.url, currentNextPage
- )
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- ////////////////////////////////////////////////////////////////////////// */
- override fun getItemViewMode(): ItemViewMode {
- return ItemViewMode.LIST
+ override fun onDestroyView() {
+ super.onDestroyView()
+ disposables.clear()
}
companion object {
@JvmField
- val TAG: String = CommentRepliesFragment::class.java.simpleName
+ val TAG = CommentRepliesFragment::class.simpleName!!
+
+ const val COMMENT_KEY = "comment"
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 4c4dc53b3..5a2a3ede0 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -136,14 +136,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
}
}
-@Preview(
- name = "Light mode",
- uiMode = Configuration.UI_MODE_NIGHT_NO
-)
-@Preview(
- name = "Dark mode",
- uiMode = Configuration.UI_MODE_NIGHT_YES
-)
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CommentRepliesHeaderPreview() {
val disposables = CompositeDisposable()
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java
deleted file mode 100644
index cc160c395..000000000
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesInfo.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.schabi.newpipe.fragments.list.comments;
-
-import org.schabi.newpipe.extractor.ListInfo;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
-import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
-
-import java.util.Collections;
-
-public final class CommentRepliesInfo extends ListInfo {
- /**
- * This class is used to wrap the comment replies page into a ListInfo object.
- *
- * @param comment the comment from which to get replies
- * @param name will be shown as the fragment title
- */
- public CommentRepliesInfo(final CommentsInfoItem comment, final String name) {
- super(comment.getServiceId(),
- new ListLinkHandler("", "", "", Collections.emptyList(), null), name);
- setNextPage(comment.getReplies());
- setRelatedItems(Collections.emptyList()); // since it must be non-null
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
new file mode 100644
index 000000000..ed602d917
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
@@ -0,0 +1,22 @@
+package org.schabi.newpipe.fragments.list.comments
+
+import androidx.paging.PagingState
+import androidx.paging.rxjava3.RxPagingSource
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import org.schabi.newpipe.extractor.Page
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.util.ExtractorHelper
+
+class CommentRepliesSource(
+ private val commentsInfoItem: CommentsInfoItem,
+) : RxPagingSource() {
+ override fun loadSingle(params: LoadParams): Single> {
+ val nextPage = params.key ?: commentsInfoItem.replies
+ return ExtractorHelper.getMoreCommentItems(commentsInfoItem.serviceId, commentsInfoItem.url, nextPage)
+ .subscribeOn(Schedulers.io())
+ .map { LoadResult.Page(it.items, null, it.nextPage) }
+ }
+
+ override fun getRefreshKey(state: PagingState) = null
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
index 61721d546..e01cf620e 100644
--- a/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
+++ b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
@@ -1,9 +1,25 @@
package org.schabi.newpipe.ktx
+import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import androidx.core.os.BundleCompat
+import java.io.Serializable
+import kotlin.reflect.safeCast
inline fun Bundle.parcelableArrayList(key: String?): ArrayList? {
return BundleCompat.getParcelableArrayList(this, key, T::class.java)
}
+
+inline fun Bundle.serializable(key: String?): T? {
+ return getSerializable(this, key, T::class.java)
+}
+
+fun getSerializable(bundle: Bundle, key: String?, clazz: Class): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ bundle.getSerializable(key, clazz)
+ } else {
+ @Suppress("DEPRECATION")
+ clazz.kotlin.safeCast(bundle.getSerializable(key))
+ }
+}
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 5dee32371..9accf22a5 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -9,6 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
@@ -503,8 +504,11 @@ public final class NavigationHelper {
public static void openCommentRepliesFragment(@NonNull final FragmentActivity activity,
@NonNull final CommentsInfoItem comment) {
+ final var bundle = new Bundle();
+ bundle.putSerializable(CommentRepliesFragment.COMMENT_KEY, comment);
+
defaultTransaction(activity.getSupportFragmentManager())
- .replace(R.id.fragment_holder, new CommentRepliesFragment(comment),
+ .replace(R.id.fragment_holder, CommentRepliesFragment.class, bundle,
CommentRepliesFragment.TAG)
.addToBackStack(CommentRepliesFragment.TAG)
.commit();
diff --git a/build.gradle b/build.gradle
index 5a1ae1945..49de98659 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.9.23'
+ ext.kotlin_version = '1.9.24'
repositories {
google()
mavenCentral()
From 11bb2495ba9bcd555146bdcf2dd8b810d9287e35 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 18 Jun 2024 09:34:12 +0530
Subject: [PATCH 20/93] Improve previews, display date of comment
---
.../newpipe/fragments/list/comments/Comment.kt | 15 +++++++++++----
.../fragments/list/comments/CommentReplies.kt | 3 +++
.../list/comments/CommentRepliesHeader.kt | 17 ++++++++---------
3 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index bdffa8561..78c9f88f4 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -33,6 +33,7 @@ import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
@@ -68,8 +69,11 @@ fun Comment(comment: CommentsInfoItem) {
modifier = Modifier.clickable { isExpanded = !isExpanded },
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
+ val date = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
Text(
- text = comment.uploaderName,
+ text = Localization.concatenateStrings(comment.uploaderName, date),
color = MaterialTheme.colorScheme.secondary
)
@@ -118,9 +122,9 @@ fun CommentsInfoItem(
commentText: Description,
uploaderName: String,
textualUploadDate: String = "5 months ago",
- likeCount: Int = 100,
- isHeartedByUploader: Boolean = true,
- isPinned: Boolean = true,
+ likeCount: Int = 0,
+ isHeartedByUploader: Boolean = false,
+ isPinned: Boolean = false,
) = CommentsInfoItem(serviceId, url, name).apply {
this.commentText = commentText
this.uploaderName = uploaderName
@@ -137,6 +141,9 @@ private fun CommentPreview() {
val comment = CommentsInfoItem(
commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
uploaderName = "Test",
+ likeCount = 100,
+ isPinned = true,
+ isHeartedByUploader = true
)
AppTheme {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
index 05a6d1c3b..42813f087 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
@@ -42,6 +42,9 @@ private fun CommentRepliesPreview() {
val comment = CommentsInfoItem(
commentText = Description("Hello world!", Description.PLAIN_TEXT),
uploaderName = "Test",
+ likeCount = 100,
+ isPinned = true,
+ isHeartedByUploader = true
)
val reply1 = CommentsInfoItem(
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 5a2a3ede0..1853a134b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -140,16 +140,15 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CommentRepliesHeaderPreview() {
- val disposables = CompositeDisposable()
- val comment = CommentsInfoItem(1, "", "")
- comment.commentText = Description("Hello world!", Description.PLAIN_TEXT)
- comment.uploaderName = "Test"
- comment.textualUploadDate = "5 months ago"
- comment.likeCount = 100
- comment.isPinned = true
- comment.isHeartedByUploader = true
+ val comment = CommentsInfoItem(
+ commentText = Description("Hello world!", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ likeCount = 100,
+ isPinned = true,
+ isHeartedByUploader = true
+ )
AppTheme {
- CommentRepliesHeader(comment, disposables)
+ CommentRepliesHeader(comment, CompositeDisposable())
}
}
From e30d5e43050eaf2d4f7a6ec77bdf83f227d6ad26 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 18 Jun 2024 16:13:20 +0530
Subject: [PATCH 21/93] Fixed some comment issues
---
.../fragments/list/comments/Comment.kt | 44 ++++++++++---------
.../fragments/list/comments/CommentReplies.kt | 16 +++----
2 files changed, 32 insertions(+), 28 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index 78c9f88f4..bde37ac95 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -40,9 +41,15 @@ import org.schabi.newpipe.util.image.ImageStrategy
@Composable
fun Comment(comment: CommentsInfoItem) {
val context = LocalContext.current
+ var isExpanded by rememberSaveable { mutableStateOf(false) }
Surface(color = MaterialTheme.colorScheme.background) {
- Row(modifier = Modifier.padding(all = 8.dp)) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { isExpanded = !isExpanded }
+ .padding(all = 8.dp)
+ ) {
if (ImageStrategy.shouldLoadImages()) {
AsyncImage(
model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
@@ -63,19 +70,23 @@ fun Comment(comment: CommentsInfoItem) {
Spacer(modifier = Modifier.width(8.dp))
- var isExpanded by rememberSaveable { mutableStateOf(false) }
+ Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
+ Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
+ }
- Column(
- modifier = Modifier.clickable { isExpanded = !isExpanded },
- verticalArrangement = Arrangement.spacedBy(4.dp)
- ) {
- val date = Localization.relativeTimeOrTextual(
- context, comment.uploadDate, comment.textualUploadDate
- )
- Text(
- text = Localization.concatenateStrings(comment.uploaderName, date),
- color = MaterialTheme.colorScheme.secondary
- )
+ val date = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
+ Text(
+ text = Localization.concatenateStrings(comment.uploaderName, date),
+ color = MaterialTheme.colorScheme.secondary
+ )
+ }
// TODO: Handle HTML and Markdown formats.
Text(
@@ -100,13 +111,6 @@ fun Comment(comment: CommentsInfoItem) {
contentDescription = stringResource(R.string.detail_heart_img_view_description)
)
}
-
- if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
- )
- }
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
index 42813f087..dcabedb48 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
@@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.list.comments
import android.content.res.Configuration
-import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
@@ -24,13 +23,14 @@ fun CommentReplies(
) {
val replies = flow.collectAsLazyPagingItems()
- Column {
- CommentRepliesHeader(comment = comment, disposables = disposables)
- HorizontalDivider(thickness = 1.dp)
- LazyColumn {
- items(replies.itemCount) {
- Comment(comment = replies[it]!!)
- }
+ LazyColumn {
+ item {
+ CommentRepliesHeader(comment = comment, disposables = disposables)
+ HorizontalDivider(thickness = 1.dp)
+ }
+
+ items(replies.itemCount) {
+ Comment(comment = replies[it]!!)
}
}
}
From 1908e18dc4e02d15983cbf35952ffa4ab04a7114 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 19 Jun 2024 12:40:49 +0530
Subject: [PATCH 22/93] Use AnnotatedString to handle HTML parsing
---
app/build.gradle | 1 +
.../fragments/list/comments/Comment.kt | 50 ++++++++++++++-----
.../fragments/list/comments/CommentReplies.kt | 8 ++-
.../list/comments/CommentRepliesFragment.kt | 9 +---
.../list/comments/CommentRepliesHeader.kt | 28 ++---------
5 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 200af2209..6e6e29efb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -293,6 +293,7 @@ dependencies {
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
+ implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString
// Paging
implementation 'androidx.paging:paging-rxjava3:3.3.0'
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index bde37ac95..0f5972879 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -18,6 +18,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@@ -25,8 +26,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ParagraphStyle
+import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
@@ -38,6 +44,18 @@ import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
+@Composable
+fun rememberParsedText(commentText: Description): AnnotatedString {
+ // TODO: Handle links and hashtags, Markdown.
+ return remember(commentText) {
+ if (commentText.type == Description.HTML) {
+ AnnotatedString.fromHtml(commentText.content)
+ } else {
+ AnnotatedString(commentText.content, ParagraphStyle())
+ }
+ }
+}
+
@Composable
fun Comment(comment: CommentsInfoItem) {
val context = LocalContext.current
@@ -79,23 +97,22 @@ fun Comment(comment: CommentsInfoItem) {
)
}
- val date = Localization.relativeTimeOrTextual(
- context, comment.uploadDate, comment.textualUploadDate
- )
- Text(
- text = Localization.concatenateStrings(comment.uploaderName, date),
- color = MaterialTheme.colorScheme.secondary
- )
+ val nameAndDate = remember(comment) {
+ val date = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
+ Localization.concatenateStrings(comment.uploaderName, date)
+ }
+ Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
}
- // TODO: Handle HTML and Markdown formats.
Text(
- text = comment.commentText.content,
+ text = rememberParsedText(comment.commentText),
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
@@ -138,12 +155,21 @@ fun CommentsInfoItem(
this.isPinned = isPinned
}
+class DescriptionPreviewProvider : PreviewParameterProvider {
+ override val values = sequenceOf(
+ Description("Hello world! This line should be hidden by default.", Description.HTML),
+ Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
+ )
+}
+
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-private fun CommentPreview() {
+private fun CommentPreview(
+ @PreviewParameter(DescriptionPreviewProvider::class) description: Description
+) {
val comment = CommentsInfoItem(
- commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
+ commentText = description,
uploaderName = "Test",
likeCount = 100,
isPinned = true,
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
index dcabedb48..53a4fa4bf 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
@@ -8,7 +8,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
-import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
@@ -18,14 +17,13 @@ import org.schabi.newpipe.ui.theme.AppTheme
@Composable
fun CommentReplies(
comment: CommentsInfoItem,
- flow: Flow>,
- disposables: CompositeDisposable
+ flow: Flow>
) {
val replies = flow.collectAsLazyPagingItems()
LazyColumn {
item {
- CommentRepliesHeader(comment = comment, disposables = disposables)
+ CommentRepliesHeader(comment = comment)
HorizontalDivider(thickness = 1.dp)
}
@@ -58,6 +56,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(listOf(reply1, reply2)))
AppTheme {
- CommentReplies(comment = comment, flow = flow, disposables = CompositeDisposable())
+ CommentReplies(comment = comment, flow = flow)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 6afbc7e20..e1ed3041e 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -9,13 +9,11 @@ import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.paging.Pager
import androidx.paging.PagingConfig
-import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.ktx.serializable
import org.schabi.newpipe.ui.theme.AppTheme
class CommentRepliesFragment : Fragment() {
- private val disposables = CompositeDisposable()
lateinit var comment: CommentsInfoItem
override fun onCreateView(
@@ -33,17 +31,12 @@ class CommentRepliesFragment : Fragment() {
}
AppTheme {
- CommentReplies(comment = comment, flow = flow, disposables = disposables)
+ CommentReplies(comment = comment, flow = flow)
}
}
}
}
- override fun onDestroyView() {
- super.onDestroyView()
- disposables.clear()
- }
-
companion object {
@JvmField
val TAG = CommentRepliesFragment::class.simpleName!!
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 1853a134b..2c93999e8 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.list.comments
import android.content.res.Configuration
-import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -25,24 +24,18 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.text.HtmlCompat
-import androidx.core.text.method.LinkMovementMethodCompat
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
-import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
-import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.image.ImageStrategy
-import org.schabi.newpipe.util.text.TextLinkifier
@Composable
-fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) {
+fun CommentRepliesHeader(comment: CommentsInfoItem) {
val context = LocalContext.current
Surface(color = MaterialTheme.colorScheme.background) {
@@ -117,20 +110,9 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
}
}
- AndroidView(
- factory = { context ->
- TextView(context).apply {
- movementMethod = LinkMovementMethodCompat.getInstance()
- }
- },
- update = { view ->
- // setup comment content
- TextLinkifier.fromDescription(
- view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
- ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
- null
- )
- }
+ Text(
+ text = rememberParsedText(comment.commentText),
+ style = MaterialTheme.typography.bodyMedium
)
}
}
@@ -149,6 +131,6 @@ fun CommentRepliesHeaderPreview() {
)
AppTheme {
- CommentRepliesHeader(comment, CompositeDisposable())
+ CommentRepliesHeader(comment)
}
}
From e92ba8f5d1f43d6dd569e4a399f6aaa8fdd0a235 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 19 Jun 2024 12:59:19 +0530
Subject: [PATCH 23/93] Add replies button
---
.../fragments/list/comments/Comment.kt | 50 ++++++++++++++-----
1 file changed, 37 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index 0f5972879..d7595f9fa 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -15,16 +15,19 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
@@ -37,6 +40,7 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
@@ -115,24 +119,38 @@ fun Comment(comment: CommentsInfoItem) {
style = MaterialTheme.typography.bodyMedium,
)
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
- )
- Text(text = comment.likeCount.toString())
-
- if (comment.isHeartedByUploader) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Image(
- painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
)
+ Text(text = comment.likeCount.toString())
+
+ if (comment.isHeartedByUploader) {
+ Image(
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ )
+ }
+ }
+
+ if (comment.replies != null) {
+ TextButton(onClick = { /*TODO*/ }) {
+ Text(
+ text = pluralStringResource(
+ R.plurals.replies, comment.replyCount, comment.replyCount.toString()
+ )
+ )
+ }
}
}
}
}
-
- // TODO: Add support for comment replies
}
}
@@ -146,6 +164,8 @@ fun CommentsInfoItem(
likeCount: Int = 0,
isHeartedByUploader: Boolean = false,
isPinned: Boolean = false,
+ replies: Page? = null,
+ replyCount: Int = 0,
) = CommentsInfoItem(serviceId, url, name).apply {
this.commentText = commentText
this.uploaderName = uploaderName
@@ -153,6 +173,8 @@ fun CommentsInfoItem(
this.likeCount = likeCount
this.isHeartedByUploader = isHeartedByUploader
this.isPinned = isPinned
+ this.replies = replies
+ this.replyCount = replyCount
}
class DescriptionPreviewProvider : PreviewParameterProvider {
@@ -173,7 +195,9 @@ private fun CommentPreview(
uploaderName = "Test",
likeCount = 100,
isPinned = true,
- isHeartedByUploader = true
+ isHeartedByUploader = true,
+ replies = Page(""),
+ replyCount = 10
)
AppTheme {
From 5841eaa6d787d8cdb15ba3a2723a3bb592fa3d99 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Thu, 20 Jun 2024 07:08:12 +0530
Subject: [PATCH 24/93] Set view strategy
---
.../newpipe/fragments/list/comments/CommentRepliesFragment.kt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index e1ed3041e..555d1e58d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.Fragment
import androidx.paging.Pager
import androidx.paging.PagingConfig
@@ -23,6 +24,7 @@ class CommentRepliesFragment : Fragment() {
): View {
comment = requireArguments().serializable(COMMENT_KEY)!!
return ComposeView(requireContext()).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val flow = remember(comment) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
From 0ec81c9e52eaf669410b43d2bd7b91be1f8fb114 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Thu, 20 Jun 2024 09:12:40 +0530
Subject: [PATCH 25/93] Fixed like count display
---
.../org/schabi/newpipe/fragments/list/comments/Comment.kt | 2 +-
.../newpipe/fragments/list/comments/CommentRepliesHeader.kt | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index d7595f9fa..36e7aebf6 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -129,7 +129,7 @@ fun Comment(comment: CommentsInfoItem) {
painter = painterResource(R.drawable.ic_thumb_up),
contentDescription = stringResource(R.string.detail_likes_img_view_description)
)
- Text(text = comment.likeCount.toString())
+ Text(text = Localization.likeCount(context, comment.likeCount))
if (comment.isHeartedByUploader) {
Image(
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 2c93999e8..e3f5295be 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -92,7 +92,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
painter = painterResource(R.drawable.ic_thumb_up),
contentDescription = stringResource(R.string.detail_likes_img_view_description)
)
- Text(text = comment.likeCount.toString())
+ Text(text = Localization.likeCount(context, comment.likeCount))
if (comment.isHeartedByUploader) {
Image(
@@ -125,7 +125,7 @@ fun CommentRepliesHeaderPreview() {
val comment = CommentsInfoItem(
commentText = Description("Hello world!", Description.PLAIN_TEXT),
uploaderName = "Test",
- likeCount = 100,
+ likeCount = 1000,
isPinned = true,
isHeartedByUploader = true
)
From 5bfb0449cfdbbaeba4446c790d0808272c56610f Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 21 Jun 2024 08:28:26 +0530
Subject: [PATCH 26/93] Fixed fragment title
---
.../fragments/list/comments/CommentRepliesFragment.kt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 555d1e58d..4a362b8f8 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
@@ -13,6 +14,7 @@ import androidx.paging.PagingConfig
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.ktx.serializable
import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.util.Localization
class CommentRepliesFragment : Fragment() {
lateinit var comment: CommentsInfoItem
@@ -23,6 +25,12 @@ class CommentRepliesFragment : Fragment() {
savedInstanceState: Bundle?
): View {
comment = requireArguments().serializable(COMMENT_KEY)!!
+
+ val activity = requireActivity() as AppCompatActivity
+ val bar = activity.supportActionBar!!
+ bar.setDisplayShowTitleEnabled(true)
+ bar.title = Localization.replyCount(activity, comment.replyCount)
+
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
From be037e075619c3f220258913671ca801ebc7311f Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 21 Jun 2024 16:05:44 +0530
Subject: [PATCH 27/93] Rename .java to .kt
---
.../list/comments/{CommentsFragment.java => CommentsFragment.kt} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename app/src/main/java/org/schabi/newpipe/fragments/list/comments/{CommentsFragment.java => CommentsFragment.kt} (100%)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
similarity index 100%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.java
rename to app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
From 9c52e039ee00b6d980920ae9abb9319160d0d643 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 21 Jun 2024 16:05:45 +0530
Subject: [PATCH 28/93] Migrate comments fragment to Jetpack Compose
---
.../fragments/detail/VideoDetailFragment.java | 17 +-
.../fragments/list/comments/Comment.kt | 9 +-
.../list/comments/CommentRepliesFragment.kt | 6 +-
.../list/comments/CommentRepliesSource.kt | 22 --
.../{CommentReplies.kt => CommentSection.kt} | 35 ++-
.../list/comments/CommentsFragment.kt | 162 ++++----------
.../fragments/list/comments/CommentsSource.kt | 36 +++
.../newpipe/info_list/InfoItemBuilder.java | 26 +--
.../newpipe/info_list/InfoListAdapter.java | 61 ++---
.../holder/CommentInfoItemHolder.java | 208 ------------------
.../schabi/newpipe/util/ExtractorHelper.java | 9 -
app/src/main/res/layout/fragment_comments.xml | 71 ------
app/src/main/res/layout/list_comment_item.xml | 104 ---------
13 files changed, 161 insertions(+), 605 deletions(-)
delete mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
rename app/src/main/java/org/schabi/newpipe/fragments/list/comments/{CommentReplies.kt => CommentSection.kt} (62%)
create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
delete mode 100644 app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java
delete mode 100644 app/src/main/res/layout/fragment_comments.xml
delete mode 100644 app/src/main/res/layout/list_comment_item.xml
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 11a315d69..a364c42cd 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -882,7 +882,7 @@ public final class VideoDetailFragment
if (shouldShowComments()) {
pageAdapter.addFragment(
- CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
+ CommentsFragment.getInstance(serviceId, url), COMMENTS_TAB_TAG);
tabIcons.add(R.drawable.ic_comment);
tabContentDescriptions.add(R.string.comments_tab_description);
}
@@ -1014,16 +1014,15 @@ public final class VideoDetailFragment
public void scrollToComment(final CommentsInfoItem comment) {
final int commentsTabPos = pageAdapter.getItemPositionByTitle(COMMENTS_TAB_TAG);
- final Fragment fragment = pageAdapter.getItem(commentsTabPos);
- if (!(fragment instanceof CommentsFragment)) {
- return;
- }
+ final var fragment = pageAdapter.getItem(commentsTabPos);
+ // TODO: Implement the scrolling with Compose.
// unexpand the app bar only if scrolling to the comment succeeded
- if (((CommentsFragment) fragment).scrollToComment(comment)) {
- binding.appBarLayout.setExpanded(false, false);
- binding.viewPager.setCurrentItem(commentsTabPos, false);
- }
+// if (fragment instanceof CommentsFragment commentsFragment &&
+// commentsFragment.scrollToComment(comment)) {
+// binding.appBarLayout.setExpanded(false, false);
+// binding.viewPager.setCurrentItem(commentsTabPos, false);
+// }
}
/*//////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index 36e7aebf6..ac254a5b3 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -83,8 +83,7 @@ fun Comment(comment: CommentsInfoItem) {
.clip(CircleShape)
.clickable {
NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity,
- comment
+ context as FragmentActivity, comment
)
}
)
@@ -140,7 +139,11 @@ fun Comment(comment: CommentsInfoItem) {
}
if (comment.replies != null) {
- TextButton(onClick = { /*TODO*/ }) {
+ TextButton(onClick = {
+ NavigationHelper.openCommentRepliesFragment(
+ context as FragmentActivity, comment
+ )
+ }) {
Text(
text = pluralStringResource(
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
index 4a362b8f8..e25b3a960 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
@@ -31,17 +31,17 @@ class CommentRepliesFragment : Fragment() {
bar.setDisplayShowTitleEnabled(true)
bar.title = Localization.replyCount(activity, comment.replyCount)
- return ComposeView(requireContext()).apply {
+ return ComposeView(activity).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val flow = remember(comment) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentRepliesSource(comment)
+ CommentsSource(comment.serviceId, comment.url, comment.replies)
}.flow
}
AppTheme {
- CommentReplies(comment = comment, flow = flow)
+ CommentSection(parentComment = comment, flow = flow)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
deleted file mode 100644
index ed602d917..000000000
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesSource.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.schabi.newpipe.fragments.list.comments
-
-import androidx.paging.PagingState
-import androidx.paging.rxjava3.RxPagingSource
-import io.reactivex.rxjava3.core.Single
-import io.reactivex.rxjava3.schedulers.Schedulers
-import org.schabi.newpipe.extractor.Page
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem
-import org.schabi.newpipe.util.ExtractorHelper
-
-class CommentRepliesSource(
- private val commentsInfoItem: CommentsInfoItem,
-) : RxPagingSource() {
- override fun loadSingle(params: LoadParams): Single> {
- val nextPage = params.key ?: commentsInfoItem.replies
- return ExtractorHelper.getMoreCommentItems(commentsInfoItem.serviceId, commentsInfoItem.url, nextPage)
- .subscribeOn(Schedulers.io())
- .map { LoadResult.Page(it.items, null, it.nextPage) }
- }
-
- override fun getRefreshKey(state: PagingState) = null
-}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
similarity index 62%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
rename to app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
index 53a4fa4bf..45a8b5e72 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
@@ -15,16 +15,18 @@ import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
@Composable
-fun CommentReplies(
- comment: CommentsInfoItem,
- flow: Flow>
+fun CommentSection(
+ flow: Flow>,
+ parentComment: CommentsInfoItem? = null,
) {
val replies = flow.collectAsLazyPagingItems()
LazyColumn {
- item {
- CommentRepliesHeader(comment = comment)
- HorizontalDivider(thickness = 1.dp)
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
}
items(replies.itemCount) {
@@ -33,6 +35,25 @@ fun CommentReplies(
}
}
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CommentSectionPreview() {
+ val comment1 = CommentsInfoItem(
+ commentText = Description("This is a comment", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ )
+ val comment2 = CommentsInfoItem(
+ commentText = Description("This is another comment. This is another line.", Description.HTML),
+ uploaderName = "Test 2",
+ )
+ val flow = flowOf(PagingData.from(listOf(comment1, comment2)))
+
+ AppTheme {
+ CommentSection(flow = flow)
+ }
+}
+
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
@@ -56,6 +77,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(listOf(reply1, reply2)))
AppTheme {
- CommentReplies(comment = comment, flow = flow)
+ CommentSection(parentComment = comment, flow = flow)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index e25e02794..decd9391c 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -1,123 +1,55 @@
-package org.schabi.newpipe.fragments.list.comments;
+package org.schabi.newpipe.fragments.list.comments
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.util.NO_SERVICE_ID
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+class CommentsFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val arguments = requireArguments()
+ val serviceId = arguments.getInt(SERVICE_ID, NO_SERVICE_ID)
+ val url = arguments.getString(URL)
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.error.UserAction;
-import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.comments.CommentsInfo;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
-import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
-import org.schabi.newpipe.info_list.ItemViewMode;
-import org.schabi.newpipe.ktx.ViewUtils;
-import org.schabi.newpipe.util.ExtractorHelper;
+ return ComposeView(requireContext()).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ val flow = remember(serviceId, url) {
+ Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentsSource(serviceId, url, null)
+ }.flow
+ }
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.disposables.CompositeDisposable;
-
-public class CommentsFragment extends BaseListInfoFragment {
- private final CompositeDisposable disposables = new CompositeDisposable();
-
- private TextView emptyStateDesc;
-
- public static CommentsFragment getInstance(final int serviceId, final String url,
- final String name) {
- final CommentsFragment instance = new CommentsFragment();
- instance.setInitialData(serviceId, url, name);
- return instance;
- }
-
- public CommentsFragment() {
- super(UserAction.REQUESTED_COMMENTS);
- }
-
- @Override
- protected void initViews(final View rootView, final Bundle savedInstanceState) {
- super.initViews(rootView, savedInstanceState);
-
- emptyStateDesc = rootView.findViewById(R.id.empty_state_desc);
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // LifeCycle
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public View onCreateView(@NonNull final LayoutInflater inflater,
- @Nullable final ViewGroup container,
- @Nullable final Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_comments, container, false);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- disposables.clear();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Load and handle
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- protected Single> loadMoreItemsLogic() {
- return ExtractorHelper.getMoreCommentItems(serviceId, currentInfo, currentNextPage);
- }
-
- @Override
- protected Single loadResult(final boolean forceLoad) {
- return ExtractorHelper.getCommentsInfo(serviceId, url, forceLoad);
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Contract
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void handleResult(@NonNull final CommentsInfo result) {
- super.handleResult(result);
-
- emptyStateDesc.setText(
- result.isCommentsDisabled()
- ? R.string.comments_are_disabled
- : R.string.no_comments);
-
- ViewUtils.slideUp(requireView(), 120, 150, 0.06f);
- disposables.clear();
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
-
- @Override
- public void setTitle(final String title) { }
-
- @Override
- public void onCreateOptionsMenu(@NonNull final Menu menu,
- @NonNull final MenuInflater inflater) { }
-
- @Override
- protected ItemViewMode getItemViewMode() {
- return ItemViewMode.LIST;
- }
-
- public boolean scrollToComment(final CommentsInfoItem comment) {
- final int position = infoListAdapter.getItemsList().indexOf(comment);
- if (position < 0) {
- return false;
+ AppTheme {
+ CommentSection(flow = flow)
+ }
+ }
}
+ }
- itemsList.scrollToPosition(position);
- return true;
+ companion object {
+ private const val SERVICE_ID = "serviceId"
+ private const val URL = "url"
+
+ @JvmStatic
+ fun getInstance(serviceId: Int, url: String?) = CommentsFragment().apply {
+ arguments = bundleOf(
+ SERVICE_ID to serviceId,
+ URL to url
+ )
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
new file mode 100644
index 000000000..6288efaec
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
@@ -0,0 +1,36 @@
+package org.schabi.newpipe.fragments.list.comments
+
+import androidx.paging.PagingState
+import androidx.paging.rxjava3.RxPagingSource
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.schedulers.Schedulers
+import org.schabi.newpipe.extractor.Page
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.util.ExtractorHelper
+
+class CommentsSource(
+ private val serviceId: Int,
+ private val url: String?,
+ private val repliesPage: Page?
+) : RxPagingSource() {
+ override fun loadSingle(params: LoadParams): Single> {
+ // repliesPage is non-null only when used to load the comment replies
+ val nextKey = params.key ?: repliesPage
+
+ return nextKey?.let {
+ ExtractorHelper.getMoreCommentItems(serviceId, url, it)
+ .subscribeOn(Schedulers.io())
+ .map { LoadResult.Page(it.items, null, it.nextPage) }
+ } ?: ExtractorHelper.getCommentsInfo(serviceId, url, false)
+ .subscribeOn(Schedulers.io())
+ .map {
+ if (it.isCommentsDisabled) {
+ LoadResult.Invalid()
+ } else {
+ LoadResult.Page(it.relatedItems, null, it.nextPage)
+ }
+ }
+ }
+
+ override fun getRefreshKey(state: PagingState) = null
+}
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java
index d959c6327..a1526af28 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java
@@ -13,7 +13,6 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
-import org.schabi.newpipe.info_list.holder.CommentInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
@@ -75,21 +74,16 @@ public class InfoItemBuilder {
private InfoItemHolder holderFromInfoType(@NonNull final ViewGroup parent,
@NonNull final InfoItem.InfoType infoType,
final boolean useMiniVariant) {
- switch (infoType) {
- case STREAM:
- return useMiniVariant ? new StreamMiniInfoItemHolder(this, parent)
- : new StreamInfoItemHolder(this, parent);
- case CHANNEL:
- return useMiniVariant ? new ChannelMiniInfoItemHolder(this, parent)
- : new ChannelInfoItemHolder(this, parent);
- case PLAYLIST:
- return useMiniVariant ? new PlaylistMiniInfoItemHolder(this, parent)
- : new PlaylistInfoItemHolder(this, parent);
- case COMMENT:
- return new CommentInfoItemHolder(this, parent);
- default:
- throw new RuntimeException("InfoType not expected = " + infoType.name());
- }
+ return switch (infoType) {
+ case STREAM -> useMiniVariant ? new StreamMiniInfoItemHolder(this, parent)
+ : new StreamInfoItemHolder(this, parent);
+ case CHANNEL -> useMiniVariant ? new ChannelMiniInfoItemHolder(this, parent)
+ : new ChannelInfoItemHolder(this, parent);
+ case PLAYLIST -> useMiniVariant ? new PlaylistMiniInfoItemHolder(this, parent)
+ : new PlaylistInfoItemHolder(this, parent);
+ case COMMENT ->
+ throw new IllegalArgumentException("Comments should be rendered using Compose");
+ };
}
public Context getContext() {
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java
index 575568c00..e7cf9ba9a 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java
@@ -21,7 +21,6 @@ import org.schabi.newpipe.info_list.holder.ChannelCardInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
-import org.schabi.newpipe.info_list.holder.CommentInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistCardInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder;
@@ -283,46 +282,32 @@ public class InfoListAdapter extends RecyclerView.Adapter new HFHolder(headerSupplier.get());
+ case FOOTER_TYPE -> new HFHolder(PignateFooterBinding
+ .inflate(layoutInflater, parent, false)
+ .getRoot()
+ );
+ case MINI_STREAM_HOLDER_TYPE -> new StreamMiniInfoItemHolder(infoItemBuilder, parent);
+ case STREAM_HOLDER_TYPE -> new StreamInfoItemHolder(infoItemBuilder, parent);
+ case GRID_STREAM_HOLDER_TYPE -> new StreamGridInfoItemHolder(infoItemBuilder, parent);
+ case CARD_STREAM_HOLDER_TYPE -> new StreamCardInfoItemHolder(infoItemBuilder, parent);
+ case MINI_CHANNEL_HOLDER_TYPE -> new ChannelMiniInfoItemHolder(infoItemBuilder, parent);
+ case CHANNEL_HOLDER_TYPE -> new ChannelInfoItemHolder(infoItemBuilder, parent);
+ case CARD_CHANNEL_HOLDER_TYPE -> new ChannelCardInfoItemHolder(infoItemBuilder, parent);
+ case GRID_CHANNEL_HOLDER_TYPE -> new ChannelGridInfoItemHolder(infoItemBuilder, parent);
+ case MINI_PLAYLIST_HOLDER_TYPE ->
+ new PlaylistMiniInfoItemHolder(infoItemBuilder, parent);
+ case PLAYLIST_HOLDER_TYPE -> new PlaylistInfoItemHolder(infoItemBuilder, parent);
+ case GRID_PLAYLIST_HOLDER_TYPE ->
+ new PlaylistGridInfoItemHolder(infoItemBuilder, parent);
+ case CARD_PLAYLIST_HOLDER_TYPE ->
+ new PlaylistCardInfoItemHolder(infoItemBuilder, parent);
+ default -> new FallbackViewHolder(new View(parent.getContext()));
+ };
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java
deleted file mode 100644
index a3316d3fe..000000000
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package org.schabi.newpipe.info_list.holder;
-
-import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
-import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine;
-
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
-import android.text.style.ClickableSpan;
-import android.text.style.URLSpan;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.FragmentActivity;
-
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.InfoItem;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
-import org.schabi.newpipe.info_list.InfoItemBuilder;
-import org.schabi.newpipe.local.history.HistoryRecordManager;
-import org.schabi.newpipe.util.DeviceUtils;
-import org.schabi.newpipe.util.Localization;
-import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.external_communication.ShareUtils;
-import org.schabi.newpipe.util.image.CoilHelper;
-import org.schabi.newpipe.util.image.ImageStrategy;
-import org.schabi.newpipe.util.text.TextEllipsizer;
-
-public class CommentInfoItemHolder extends InfoItemHolder {
-
- private static final int COMMENT_DEFAULT_LINES = 2;
- private final int commentHorizontalPadding;
- private final int commentVerticalPadding;
-
- private final RelativeLayout itemRoot;
- private final ImageView itemThumbnailView;
- private final TextView itemContentView;
- private final ImageView itemThumbsUpView;
- private final TextView itemLikesCountView;
- private final TextView itemTitleView;
- private final ImageView itemHeartView;
- private final ImageView itemPinnedView;
- private final Button repliesButton;
-
- @NonNull
- private final TextEllipsizer textEllipsizer;
-
- public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder,
- final ViewGroup parent) {
- super(infoItemBuilder, R.layout.list_comment_item, parent);
-
- itemRoot = itemView.findViewById(R.id.itemRoot);
- itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
- itemContentView = itemView.findViewById(R.id.itemCommentContentView);
- itemThumbsUpView = itemView.findViewById(R.id.detail_thumbs_up_img_view);
- itemLikesCountView = itemView.findViewById(R.id.detail_thumbs_up_count_view);
- itemTitleView = itemView.findViewById(R.id.itemTitleView);
- itemHeartView = itemView.findViewById(R.id.detail_heart_image_view);
- itemPinnedView = itemView.findViewById(R.id.detail_pinned_view);
- repliesButton = itemView.findViewById(R.id.replies_button);
-
- commentHorizontalPadding = (int) infoItemBuilder.getContext()
- .getResources().getDimension(R.dimen.comments_horizontal_padding);
- commentVerticalPadding = (int) infoItemBuilder.getContext()
- .getResources().getDimension(R.dimen.comments_vertical_padding);
-
- textEllipsizer = new TextEllipsizer(itemContentView, COMMENT_DEFAULT_LINES, null);
- textEllipsizer.setStateChangeListener(isEllipsized -> {
- if (Boolean.TRUE.equals(isEllipsized)) {
- denyLinkFocus();
- } else {
- determineMovementMethod();
- }
- });
- }
-
- @Override
- public void updateFromItem(final InfoItem infoItem,
- final HistoryRecordManager historyRecordManager) {
- if (!(infoItem instanceof CommentsInfoItem item)) {
- return;
- }
-
- // load the author avatar
- CoilHelper.INSTANCE.loadAvatar(itemThumbnailView, item.getUploaderAvatars());
- if (ImageStrategy.shouldLoadImages()) {
- itemThumbnailView.setVisibility(View.VISIBLE);
- itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding,
- commentVerticalPadding, commentVerticalPadding);
- } else {
- itemThumbnailView.setVisibility(View.GONE);
- itemRoot.setPadding(commentHorizontalPadding, commentVerticalPadding,
- commentHorizontalPadding, commentVerticalPadding);
- }
- itemThumbnailView.setOnClickListener(view -> openCommentAuthor(item));
-
-
- // setup the top row, with pinned icon, author name and comment date
- itemPinnedView.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE);
- itemTitleView.setText(Localization.concatenateStrings(item.getUploaderName(),
- Localization.relativeTimeOrTextual(itemBuilder.getContext(), item.getUploadDate(),
- item.getTextualUploadDate())));
-
-
- // setup bottom row, with likes, heart and replies button
- itemLikesCountView.setText(
- Localization.likeCount(itemBuilder.getContext(), item.getLikeCount()));
-
- itemHeartView.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE);
-
- final boolean hasReplies = item.getReplies() != null;
- repliesButton.setOnClickListener(hasReplies ? v -> openCommentReplies(item) : null);
- repliesButton.setVisibility(hasReplies ? View.VISIBLE : View.GONE);
- repliesButton.setText(hasReplies
- ? Localization.replyCount(itemBuilder.getContext(), item.getReplyCount()) : "");
- ((RelativeLayout.LayoutParams) itemThumbsUpView.getLayoutParams()).topMargin =
- hasReplies ? 0 : DeviceUtils.dpToPx(6, itemBuilder.getContext());
-
-
- // setup comment content and click listeners to expand/ellipsize it
- textEllipsizer.setStreamingService(getServiceById(item.getServiceId()));
- textEllipsizer.setStreamUrl(item.getUrl());
- textEllipsizer.setContent(item.getCommentText());
- textEllipsizer.ellipsize();
-
- //noinspection ClickableViewAccessibility
- itemContentView.setOnTouchListener((v, event) -> {
- final CharSequence text = itemContentView.getText();
- if (text instanceof Spanned buffer) {
- final int action = event.getAction();
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
- final int offset = getOffsetForHorizontalLine(itemContentView, event);
- final var links = buffer.getSpans(offset, offset, ClickableSpan.class);
-
- if (links.length != 0) {
- if (action == MotionEvent.ACTION_UP) {
- links[0].onClick(itemContentView);
- }
- // we handle events that intersect links, so return true
- return true;
- }
- }
- }
- return false;
- });
-
- itemView.setOnClickListener(view -> {
- textEllipsizer.toggle();
- if (itemBuilder.getOnCommentsSelectedListener() != null) {
- itemBuilder.getOnCommentsSelectedListener().selected(item);
- }
- });
-
- itemView.setOnLongClickListener(view -> {
- if (DeviceUtils.isTv(itemBuilder.getContext())) {
- openCommentAuthor(item);
- } else {
- final CharSequence text = itemContentView.getText();
- if (text != null) {
- ShareUtils.copyToClipboard(itemBuilder.getContext(), text.toString());
- }
- }
- return true;
- });
- }
-
- private void openCommentAuthor(@NonNull final CommentsInfoItem item) {
- NavigationHelper.openCommentAuthorIfPresent((FragmentActivity) itemBuilder.getContext(),
- item);
- }
-
- private void openCommentReplies(@NonNull final CommentsInfoItem item) {
- NavigationHelper.openCommentRepliesFragment((FragmentActivity) itemBuilder.getContext(),
- item);
- }
-
- private void allowLinkFocus() {
- itemContentView.setMovementMethod(LinkMovementMethod.getInstance());
- }
-
- private void denyLinkFocus() {
- itemContentView.setMovementMethod(null);
- }
-
- private boolean shouldFocusLinks() {
- if (itemView.isInTouchMode()) {
- return false;
- }
-
- final URLSpan[] urls = itemContentView.getUrls();
-
- return urls != null && urls.length != 0;
- }
-
- private void determineMovementMethod() {
- if (shouldFocusLinks()) {
- allowLinkFocus();
- } else {
- denyLinkFocus();
- }
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
index 066d5f570..abf8d24c1 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
@@ -155,15 +155,6 @@ public final class ExtractorHelper {
CommentsInfo.getInfo(NewPipe.getService(serviceId), url)));
}
- public static Single> getMoreCommentItems(
- final int serviceId,
- final CommentsInfo info,
- final Page nextPage) {
- checkServiceId(serviceId);
- return Single.fromCallable(() ->
- CommentsInfo.getMoreItems(NewPipe.getService(serviceId), info, nextPage));
- }
-
public static Single> getMoreCommentItems(
final int serviceId,
final String url,
diff --git a/app/src/main/res/layout/fragment_comments.xml b/app/src/main/res/layout/fragment_comments.xml
deleted file mode 100644
index 2a8c747cd..000000000
--- a/app/src/main/res/layout/fragment_comments.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/list_comment_item.xml b/app/src/main/res/layout/list_comment_item.xml
deleted file mode 100644
index 631ab204b..000000000
--- a/app/src/main/res/layout/list_comment_item.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 93310955f26781dd04c0be8ee68959b1c42b9292 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 21 Jun 2024 19:28:31 +0530
Subject: [PATCH 29/93] Added scrollbar to comment section
---
app/build.gradle | 1 +
.../fragments/list/comments/CommentSection.kt | 55 ++++++++++---------
2 files changed, 29 insertions(+), 27 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6e6e29efb..7bea96099 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -294,6 +294,7 @@ dependencies {
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString
+ implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
// Paging
implementation 'androidx.paging:paging-rxjava3:3.3.0'
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
index 45a8b5e72..dc4104b9d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
@@ -2,6 +2,7 @@ package org.schabi.newpipe.fragments.list.comments
import android.content.res.Configuration
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@@ -10,6 +11,8 @@ import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import my.nanihadesuka.compose.LazyColumnScrollbar
+import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
@@ -20,17 +23,20 @@ fun CommentSection(
parentComment: CommentsInfoItem? = null,
) {
val replies = flow.collectAsLazyPagingItems()
+ val listState = rememberLazyListState()
- LazyColumn {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
+ LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
+ LazyColumn(state = listState) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
}
- }
- items(replies.itemCount) {
- Comment(comment = replies[it]!!)
+ items(replies.itemCount) {
+ Comment(comment = replies[it]!!)
+ }
}
}
}
@@ -39,15 +45,13 @@ fun CommentSection(
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CommentSectionPreview() {
- val comment1 = CommentsInfoItem(
- commentText = Description("This is a comment", Description.PLAIN_TEXT),
- uploaderName = "Test",
- )
- val comment2 = CommentsInfoItem(
- commentText = Description("This is another comment. This is another line.", Description.HTML),
- uploaderName = "Test 2",
- )
- val flow = flowOf(PagingData.from(listOf(comment1, comment2)))
+ val comments = (0..100).map {
+ CommentsInfoItem(
+ commentText = Description("Comment $it", Description.PLAIN_TEXT),
+ uploaderName = "Test"
+ )
+ }
+ val flow = flowOf(PagingData.from(comments))
AppTheme {
CommentSection(flow = flow)
@@ -65,16 +69,13 @@ private fun CommentRepliesPreview() {
isPinned = true,
isHeartedByUploader = true
)
-
- val reply1 = CommentsInfoItem(
- commentText = Description("This is a reply", Description.PLAIN_TEXT),
- uploaderName = "Test 2",
- )
- val reply2 = CommentsInfoItem(
- commentText = Description("This is another reply. This is another line.", Description.HTML),
- uploaderName = "Test 3",
- )
- val flow = flowOf(PagingData.from(listOf(reply1, reply2)))
+ val replies = (0..100).map {
+ CommentsInfoItem(
+ commentText = Description("Reply $it", Description.PLAIN_TEXT),
+ uploaderName = "Test"
+ )
+ }
+ val flow = flowOf(PagingData.from(replies))
AppTheme {
CommentSection(parentComment = comment, flow = flow)
From b9dd7078ad3ae2ac1c20969fdd8b97736026b7dc Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 23 Jun 2024 08:57:51 +0530
Subject: [PATCH 30/93] Replace CommentRepliesFragment with bottom sheet
composable, improve previews
---
.../java/org/schabi/newpipe/MainActivity.java | 103 ++----------------
.../fragments/list/comments/Comment.kt | 31 ++++--
.../list/comments/CommentRepliesFragment.kt | 56 ----------
.../fragments/list/comments/CommentSection.kt | 4 +-
.../schabi/newpipe/util/NavigationHelper.java | 14 ---
5 files changed, 35 insertions(+), 173 deletions(-)
delete mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index d288ab4c6..6b95ea8a8 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -44,7 +44,6 @@ import android.widget.FrameLayout;
import android.widget.Spinner;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
@@ -52,7 +51,6 @@ import androidx.core.app.ActivityCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentContainerView;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
@@ -71,7 +69,6 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
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.comments.CommentRepliesFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
import org.schabi.newpipe.player.Player;
@@ -557,33 +554,22 @@ public class MainActivity extends AppCompatActivity {
// interacts with a fragment inside fragment_holder so all back presses should be
// handled by it
if (bottomSheetHiddenOrCollapsed()) {
- final FragmentManager fm = getSupportFragmentManager();
- final Fragment fragment = fm.findFragmentById(R.id.fragment_holder);
+ final var fm = getSupportFragmentManager();
+ final var fragment = fm.findFragmentById(R.id.fragment_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
// delegate the back press to it
- if (fragment instanceof BackPressable) {
- if (((BackPressable) fragment).onBackPressed()) {
- return;
- }
- } else if (fragment instanceof CommentRepliesFragment) {
- // expand DetailsFragment if CommentRepliesFragment was opened
- // to show the top level comments again
- // Expand DetailsFragment if CommentRepliesFragment was opened
- // and no other CommentRepliesFragments are on top of the back stack
- // to show the top level comments again.
- openDetailFragmentFromCommentReplies(fm, false);
+ if (fragment instanceof BackPressable backPressable && backPressable.onBackPressed()) {
+ return;
}
-
} else {
- final Fragment fragmentPlayer = getSupportFragmentManager()
+ final var fragmentPlayer = getSupportFragmentManager()
.findFragmentById(R.id.fragment_player_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
// delegate the back press to it
- if (fragmentPlayer instanceof BackPressable) {
- if (!((BackPressable) fragmentPlayer).onBackPressed()) {
- BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
- .setState(BottomSheetBehavior.STATE_COLLAPSED);
- }
+ if (fragmentPlayer instanceof BackPressable backPressable
+ && !backPressable.onBackPressed()) {
+ BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
+ .setState(BottomSheetBehavior.STATE_COLLAPSED);
return;
}
}
@@ -647,15 +633,9 @@ public class MainActivity extends AppCompatActivity {
*
*/
private void onHomeButtonPressed() {
- final FragmentManager fm = getSupportFragmentManager();
- final Fragment fragment = fm.findFragmentById(R.id.fragment_holder);
+ final var fm = getSupportFragmentManager();
- if (fragment instanceof CommentRepliesFragment) {
- // Expand DetailsFragment if CommentRepliesFragment was opened
- // and no other CommentRepliesFragments are on top of the back stack
- // to show the top level comments again.
- openDetailFragmentFromCommentReplies(fm, true);
- } else if (!NavigationHelper.tryGotoSearchFragment(fm)) {
+ if (!NavigationHelper.tryGotoSearchFragment(fm)) {
// If search fragment wasn't found in the backstack go to the main fragment
NavigationHelper.gotoMainFragment(fm);
}
@@ -853,67 +833,6 @@ public class MainActivity extends AppCompatActivity {
}
}
- private void openDetailFragmentFromCommentReplies(
- @NonNull final FragmentManager fm,
- final boolean popBackStack
- ) {
- // obtain the name of the fragment under the replies fragment that's going to be popped
- @Nullable final String fragmentUnderEntryName;
- if (fm.getBackStackEntryCount() < 2) {
- fragmentUnderEntryName = null;
- } else {
- fragmentUnderEntryName = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 2)
- .getName();
- }
-
- // the root comment is the comment for which the user opened the replies page
- final var repliesFragment = (CommentRepliesFragment)
- fm.findFragmentByTag(CommentRepliesFragment.TAG);
- final var rootComment = repliesFragment == null ? null : repliesFragment.getComment();
-
- // sometimes this function pops the backstack, other times it's handled by the system
- if (popBackStack) {
- fm.popBackStackImmediate();
- }
-
- // only expand the bottom sheet back if there are no more nested comment replies fragments
- // stacked under the one that is currently being popped
- if (CommentRepliesFragment.TAG.equals(fragmentUnderEntryName)) {
- return;
- }
-
- final BottomSheetBehavior behavior = BottomSheetBehavior
- .from(mainBinding.fragmentPlayerHolder);
- // do not return to the comment if the details fragment was closed
- if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
- return;
- }
-
- // scroll to the root comment once the bottom sheet expansion animation is finished
- behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
- @Override
- public void onStateChanged(@NonNull final View bottomSheet,
- final int newState) {
- if (newState == BottomSheetBehavior.STATE_EXPANDED) {
- final Fragment detailFragment = fm.findFragmentById(
- R.id.fragment_player_holder);
- if (detailFragment instanceof VideoDetailFragment && rootComment != null) {
- // should always be the case
- ((VideoDetailFragment) detailFragment).scrollToComment(rootComment);
- }
- behavior.removeBottomSheetCallback(this);
- }
- }
-
- @Override
- public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
- // not needed, listener is removed once the sheet is expanded
- }
- });
-
- behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
- }
-
private boolean bottomSheetHiddenOrCollapsed() {
final BottomSheetBehavior bottomSheetBehavior =
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index ac254a5b3..bd00b7bd8 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -12,7 +12,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -38,6 +40,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
@@ -60,10 +64,12 @@ fun rememberParsedText(commentText: Description): AnnotatedString {
}
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Comment(comment: CommentsInfoItem) {
val context = LocalContext.current
var isExpanded by rememberSaveable { mutableStateOf(false) }
+ var showReplies by rememberSaveable { mutableStateOf(false) }
Surface(color = MaterialTheme.colorScheme.background) {
Row(
@@ -139,22 +145,29 @@ fun Comment(comment: CommentsInfoItem) {
}
if (comment.replies != null) {
- TextButton(onClick = {
- NavigationHelper.openCommentRepliesFragment(
- context as FragmentActivity, comment
- )
- }) {
- Text(
- text = pluralStringResource(
- R.plurals.replies, comment.replyCount, comment.replyCount.toString()
- )
+ TextButton(onClick = { showReplies = true }) {
+ val text = pluralStringResource(
+ R.plurals.replies, comment.replyCount, comment.replyCount.toString()
)
+ Text(text = text)
}
}
}
}
}
}
+
+ if (showReplies) {
+ ModalBottomSheet(onDismissRequest = { showReplies = false }) {
+ val flow = remember(comment) {
+ Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentsSource(comment.serviceId, comment.url, comment.replies)
+ }.flow
+ }
+
+ CommentSection(parentComment = comment, flow = flow)
+ }
+ }
}
fun CommentsInfoItem(
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
deleted file mode 100644
index e25b3a960..000000000
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.schabi.newpipe.fragments.list.comments
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.fragment.app.Fragment
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem
-import org.schabi.newpipe.ktx.serializable
-import org.schabi.newpipe.ui.theme.AppTheme
-import org.schabi.newpipe.util.Localization
-
-class CommentRepliesFragment : Fragment() {
- lateinit var comment: CommentsInfoItem
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- comment = requireArguments().serializable(COMMENT_KEY)!!
-
- val activity = requireActivity() as AppCompatActivity
- val bar = activity.supportActionBar!!
- bar.setDisplayShowTitleEnabled(true)
- bar.title = Localization.replyCount(activity, comment.replyCount)
-
- return ComposeView(activity).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- val flow = remember(comment) {
- Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentsSource(comment.serviceId, comment.url, comment.replies)
- }.flow
- }
-
- AppTheme {
- CommentSection(parentComment = comment, flow = flow)
- }
- }
- }
- }
-
- companion object {
- @JvmField
- val TAG = CommentRepliesFragment::class.simpleName!!
-
- const val COMMENT_KEY = "comment"
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
index dc4104b9d..ed1969b68 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
@@ -45,7 +45,7 @@ fun CommentSection(
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CommentSectionPreview() {
- val comments = (0..100).map {
+ val comments = (1..100).map {
CommentsInfoItem(
commentText = Description("Comment $it", Description.PLAIN_TEXT),
uploaderName = "Test"
@@ -69,7 +69,7 @@ private fun CommentRepliesPreview() {
isPinned = true,
isHeartedByUploader = true
)
- val replies = (0..100).map {
+ val replies = (1..100).map {
CommentsInfoItem(
commentText = Description("Reply $it", Description.PLAIN_TEXT),
uploaderName = "Test"
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 9accf22a5..e6af64ffe 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -9,7 +9,6 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
-import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
@@ -46,7 +45,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
-import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
@@ -502,18 +500,6 @@ public final class NavigationHelper {
}
}
- public static void openCommentRepliesFragment(@NonNull final FragmentActivity activity,
- @NonNull final CommentsInfoItem comment) {
- final var bundle = new Bundle();
- bundle.putSerializable(CommentRepliesFragment.COMMENT_KEY, comment);
-
- defaultTransaction(activity.getSupportFragmentManager())
- .replace(R.id.fragment_holder, CommentRepliesFragment.class, bundle,
- CommentRepliesFragment.TAG)
- .addToBackStack(CommentRepliesFragment.TAG)
- .commit();
- }
-
public static void openPlaylistFragment(final FragmentManager fragmentManager,
final int serviceId, final String url,
@NonNull final String name) {
From b092fe2c76bfa7bb7da970d44d54d121e5c25f69 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 23 Jun 2024 09:16:27 +0530
Subject: [PATCH 31/93] Replace Spacers with the horizontalArrangement
parameter
---
.../org/schabi/newpipe/fragments/list/comments/Comment.kt | 7 ++-----
.../fragments/list/comments/CommentRepliesHeader.kt | 8 ++------
2 files changed, 4 insertions(+), 11 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index bd00b7bd8..704b5d7f7 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -6,11 +6,9 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
@@ -76,7 +74,8 @@ fun Comment(comment: CommentsInfoItem) {
modifier = Modifier
.fillMaxWidth()
.clickable { isExpanded = !isExpanded }
- .padding(all = 8.dp)
+ .padding(all = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
if (ImageStrategy.shouldLoadImages()) {
AsyncImage(
@@ -95,8 +94,6 @@ fun Comment(comment: CommentsInfoItem) {
)
}
- Spacer(modifier = Modifier.width(8.dp))
-
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
if (comment.isPinned) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index e3f5295be..667c3a679 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -6,11 +6,9 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -42,6 +40,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
Column(modifier = Modifier.padding(all = 8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
@@ -53,6 +52,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
comment
)
},
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (ImageStrategy.shouldLoadImages()) {
@@ -67,8 +67,6 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
)
}
- Spacer(modifier = Modifier.width(8.dp))
-
Column {
Text(text = comment.uploaderName)
@@ -82,8 +80,6 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
}
}
- Spacer(modifier = Modifier.weight(1f))
-
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
From 5e7e14ee4d3704928222673eaee3d9e6a6884c81 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 23 Jun 2024 10:53:22 +0530
Subject: [PATCH 32/93] Handle no comments and comments disabled scenarios
---
.../fragments/detail/VideoDetailFragment.java | 3 +-
.../fragments/list/comments/Comment.kt | 142 +++++++++---------
.../fragments/list/comments/CommentSection.kt | 100 +++++++++---
.../fragments/list/comments/CommentsSource.kt | 4 +-
4 files changed, 154 insertions(+), 95 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index a364c42cd..8fcf1e663 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -881,8 +881,7 @@ public final class VideoDetailFragment
tabContentDescriptions.clear();
if (shouldShowComments()) {
- pageAdapter.addFragment(
- CommentsFragment.getInstance(serviceId, url), COMMENTS_TAB_TAG);
+ pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url), COMMENTS_TAB_TAG);
tabIcons.add(R.drawable.ic_comment);
tabContentDescriptions.add(R.string.comments_tab_description);
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
index 704b5d7f7..abc8bfafe 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
@@ -69,85 +69,83 @@ fun Comment(comment: CommentsInfoItem) {
var isExpanded by rememberSaveable { mutableStateOf(false) }
var showReplies by rememberSaveable { mutableStateOf(false) }
- Surface(color = MaterialTheme.colorScheme.background) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .clickable { isExpanded = !isExpanded }
- .padding(all = 8.dp),
- horizontalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- if (ImageStrategy.shouldLoadImages()) {
- AsyncImage(
- model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
- contentDescription = null,
- placeholder = painterResource(R.drawable.placeholder_person),
- error = painterResource(R.drawable.placeholder_person),
- modifier = Modifier
- .size(42.dp)
- .clip(CircleShape)
- .clickable {
- NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity, comment
- )
- }
- )
- }
-
- Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
- Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
- if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { isExpanded = !isExpanded }
+ .padding(all = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ if (ImageStrategy.shouldLoadImages()) {
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(
+ context as FragmentActivity, comment
)
}
+ )
+ }
- val nameAndDate = remember(comment) {
- val date = Localization.relativeTimeOrTextual(
- context, comment.uploadDate, comment.textualUploadDate
- )
- Localization.concatenateStrings(comment.uploaderName, date)
- }
- Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
+ Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
+ Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
}
- Text(
- text = rememberParsedText(comment.commentText),
- // If the comment is expanded, we display all its content
- // otherwise we only display the first two lines
- maxLines = if (isExpanded) Int.MAX_VALUE else 2,
- overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyMedium,
- )
+ val nameAndDate = remember(comment) {
+ val date = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
+ Localization.concatenateStrings(comment.uploaderName, date)
+ }
+ Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
+ }
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Text(
+ text = rememberParsedText(comment.commentText),
+ // If the comment is expanded, we display all its content
+ // otherwise we only display the first two lines
+ maxLines = if (isExpanded) Int.MAX_VALUE else 2,
+ overflow = TextOverflow.Ellipsis,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Image(
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ )
+ Text(text = Localization.likeCount(context, comment.likeCount))
+
+ if (comment.isHeartedByUploader) {
Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
)
- Text(text = Localization.likeCount(context, comment.likeCount))
-
- if (comment.isHeartedByUploader) {
- Image(
- painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
- )
- }
}
+ }
- if (comment.replies != null) {
- TextButton(onClick = { showReplies = true }) {
- val text = pluralStringResource(
- R.plurals.replies, comment.replyCount, comment.replyCount.toString()
- )
- Text(text = text)
- }
+ if (comment.replies != null) {
+ TextButton(onClick = { showReplies = true }) {
+ val text = pluralStringResource(
+ R.plurals.replies, comment.replyCount, comment.replyCount.toString()
+ )
+ Text(text = text)
}
}
}
@@ -190,7 +188,7 @@ fun CommentsInfoItem(
this.replyCount = replyCount
}
-class DescriptionPreviewProvider : PreviewParameterProvider {
+private class DescriptionPreviewProvider : PreviewParameterProvider {
override val values = sequenceOf(
Description("Hello world! This line should be hidden by default.", Description.HTML),
Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
@@ -214,6 +212,8 @@ private fun CommentPreview(
)
AppTheme {
- Comment(comment)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ Comment(comment)
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
index ed1969b68..900392c09 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
@@ -1,18 +1,33 @@
package org.schabi.newpipe.fragments.list.comments
import android.content.res.Configuration
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.paging.LoadState
+import androidx.paging.LoadStates
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
+import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.theme.AppTheme
@@ -23,38 +38,81 @@ fun CommentSection(
parentComment: CommentsInfoItem? = null,
) {
val replies = flow.collectAsLazyPagingItems()
- val listState = rememberLazyListState()
+ val itemCount by remember { derivedStateOf { replies.itemCount } }
- LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
- LazyColumn(state = listState) {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ val refresh = replies.loadState.refresh
+ if (itemCount == 0 && refresh !is LoadState.Loading) {
+ NoCommentsMessage((refresh as? LoadState.Error)?.error)
+ } else {
+ val listState = rememberLazyListState()
+
+ LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
+ LazyColumn(state = listState) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
+ }
+
+ items(itemCount) {
+ Comment(comment = replies[it]!!)
+ }
}
}
-
- items(replies.itemCount) {
- Comment(comment = replies[it]!!)
- }
}
}
}
+@Composable
+private fun NoCommentsMessage(error: Throwable?) {
+ val message = if (error is CommentsDisabledException) {
+ R.string.comments_are_disabled
+ } else {
+ R.string.no_comments
+ }
+
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(text = "(╯°-°)╯", fontSize = 35.sp)
+ Text(text = stringResource(id = message), fontSize = 24.sp)
+ }
+}
+
+private class CommentDataProvider : PreviewParameterProvider> {
+ private val notLoading = LoadState.NotLoading(true)
+
+ override val values = sequenceOf(
+ // Normal view
+ PagingData.from(
+ (1..100).map {
+ CommentsInfoItem(
+ commentText = Description("Comment $it", Description.PLAIN_TEXT),
+ uploaderName = "Test"
+ )
+ }
+ ),
+ // Comments disabled
+ PagingData.from(
+ listOf(),
+ LoadStates(LoadState.Error(CommentsDisabledException()), notLoading, notLoading)
+ ),
+ // No comments
+ PagingData.from(
+ listOf(),
+ LoadStates(notLoading, notLoading, notLoading)
+ )
+ )
+}
+
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-private fun CommentSectionPreview() {
- val comments = (1..100).map {
- CommentsInfoItem(
- commentText = Description("Comment $it", Description.PLAIN_TEXT),
- uploaderName = "Test"
- )
- }
- val flow = flowOf(PagingData.from(comments))
-
+private fun CommentSectionPreview(
+ @PreviewParameter(CommentDataProvider::class) pagingData: PagingData
+) {
AppTheme {
- CommentSection(flow = flow)
+ CommentSection(flow = flowOf(pagingData))
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
index 6288efaec..9e46a9df1 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
@@ -25,7 +25,7 @@ class CommentsSource(
.subscribeOn(Schedulers.io())
.map {
if (it.isCommentsDisabled) {
- LoadResult.Invalid()
+ LoadResult.Error(CommentsDisabledException())
} else {
LoadResult.Page(it.relatedItems, null, it.nextPage)
}
@@ -34,3 +34,5 @@ class CommentsSource(
override fun getRefreshKey(state: PagingState) = null
}
+
+class CommentsDisabledException : RuntimeException()
From 909d21400207655bed17d920b59d94e72111d1de Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 26 Jun 2024 07:36:11 +0530
Subject: [PATCH 33/93] Rm redundant Surface
---
.../list/comments/CommentRepliesHeader.kt | 132 +++++++++---------
1 file changed, 66 insertions(+), 66 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
index 667c3a679..4fe69b9cc 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
@@ -36,81 +36,79 @@ import org.schabi.newpipe.util.image.ImageStrategy
fun CommentRepliesHeader(comment: CommentsInfoItem) {
val context = LocalContext.current
- Surface(color = MaterialTheme.colorScheme.background) {
- Column(modifier = Modifier.padding(all = 8.dp)) {
+ Column(modifier = Modifier.padding(all = 8.dp)) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier
+ .padding(top = 8.dp, bottom = 8.dp, end = 8.dp)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(
+ context as FragmentActivity,
+ comment
+ )
+ },
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
- Row(
- modifier = Modifier
- .padding(top = 8.dp, bottom = 8.dp, end = 8.dp)
- .clickable {
- NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity,
- comment
- )
- },
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- if (ImageStrategy.shouldLoadImages()) {
- AsyncImage(
- model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
- contentDescription = null,
- placeholder = painterResource(R.drawable.placeholder_person),
- error = painterResource(R.drawable.placeholder_person),
- modifier = Modifier
- .size(42.dp)
- .clip(CircleShape)
- )
- }
-
- Column {
- Text(text = comment.uploaderName)
-
- Text(
- color = MaterialTheme.colorScheme.secondary,
- style = MaterialTheme.typography.bodySmall,
- text = Localization.relativeTimeOrTextual(
- context, comment.uploadDate, comment.textualUploadDate
- )
- )
- }
+ if (ImageStrategy.shouldLoadImages()) {
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ )
}
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ Column {
+ Text(text = comment.uploaderName)
+
+ Text(
+ color = MaterialTheme.colorScheme.secondary,
+ style = MaterialTheme.typography.bodySmall,
+ text = Localization.relativeTimeOrTextual(
+ context, comment.uploadDate, comment.textualUploadDate
+ )
)
- Text(text = Localization.likeCount(context, comment.likeCount))
-
- if (comment.isHeartedByUploader) {
- Image(
- painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
- )
- }
-
- if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
- )
- }
}
}
- Text(
- text = rememberParsedText(comment.commentText),
- style = MaterialTheme.typography.bodyMedium
- )
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ )
+ Text(text = Localization.likeCount(context, comment.likeCount))
+
+ if (comment.isHeartedByUploader) {
+ Image(
+ painter = painterResource(R.drawable.ic_heart),
+ contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ )
+ }
+
+ if (comment.isPinned) {
+ Image(
+ painter = painterResource(R.drawable.ic_pin),
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ )
+ }
+ }
}
+
+ Text(
+ text = rememberParsedText(comment.commentText),
+ style = MaterialTheme.typography.bodyMedium
+ )
}
}
@@ -127,6 +125,8 @@ fun CommentRepliesHeaderPreview() {
)
AppTheme {
- CommentRepliesHeader(comment)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentRepliesHeader(comment)
+ }
}
}
From 369a46f8fe09b7a7e8b19d01784fb63a6bdbea9d Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 28 Jun 2024 07:57:25 +0530
Subject: [PATCH 34/93] Improve code organization
---
.../schabi/newpipe/{ui => compose}/Toolbar.kt | 6 ++---
.../comments => compose/comment}/Comment.kt | 23 ++++---------------
.../comment}/CommentRepliesHeader.kt | 7 +++---
.../comment}/CommentSection.kt | 7 +++---
.../newpipe/{ui => compose}/theme/Color.kt | 2 +-
.../{ui => compose}/theme/SizeTokens.kt | 2 +-
.../newpipe/{ui => compose}/theme/Theme.kt | 2 +-
.../newpipe/compose/util/ParseDescription.kt | 20 ++++++++++++++++
.../list/comments/CommentsFragment.kt | 4 +++-
.../comments => paging}/CommentsSource.kt | 2 +-
10 files changed, 43 insertions(+), 32 deletions(-)
rename app/src/main/java/org/schabi/newpipe/{ui => compose}/Toolbar.kt (97%)
rename app/src/main/java/org/schabi/newpipe/{fragments/list/comments => compose/comment}/Comment.kt (92%)
rename app/src/main/java/org/schabi/newpipe/{fragments/list/comments => compose/comment}/CommentRepliesHeader.kt (95%)
rename app/src/main/java/org/schabi/newpipe/{fragments/list/comments => compose/comment}/CommentSection.kt (96%)
rename app/src/main/java/org/schabi/newpipe/{ui => compose}/theme/Color.kt (98%)
rename app/src/main/java/org/schabi/newpipe/{ui => compose}/theme/SizeTokens.kt (88%)
rename app/src/main/java/org/schabi/newpipe/{ui => compose}/theme/Theme.kt (98%)
create mode 100644 app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
rename app/src/main/java/org/schabi/newpipe/{fragments/list/comments => paging}/CommentsSource.kt (96%)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt b/app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt
similarity index 97%
rename from app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
rename to app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt
index b788932a2..469d88ec0 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui
+package org.schabi.newpipe.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -27,8 +27,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import org.schabi.newpipe.R
-import org.schabi.newpipe.ui.theme.AppTheme
-import org.schabi.newpipe.ui.theme.SizeTokens
+import org.schabi.newpipe.compose.theme.AppTheme
+import org.schabi.newpipe.compose.theme.SizeTokens
@Composable
fun TextAction(text: String, modifier: Modifier = Modifier) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
similarity index 92%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
rename to app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index abc8bfafe..b63845ae1 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.fragments.list.comments
+package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
import androidx.compose.foundation.Image
@@ -29,9 +29,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ParagraphStyle
-import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -42,26 +39,16 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import coil.compose.AsyncImage
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.theme.AppTheme
+import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.paging.CommentsSource
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
-@Composable
-fun rememberParsedText(commentText: Description): AnnotatedString {
- // TODO: Handle links and hashtags, Markdown.
- return remember(commentText) {
- if (commentText.type == Description.HTML) {
- AnnotatedString.fromHtml(commentText.content)
- } else {
- AnnotatedString(commentText.content, ParagraphStyle())
- }
- }
-}
-
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Comment(comment: CommentsInfoItem) {
@@ -112,7 +99,7 @@ fun Comment(comment: CommentsInfoItem) {
}
Text(
- text = rememberParsedText(comment.commentText),
+ text = rememberParsedDescription(comment.commentText),
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
similarity index 95%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
rename to app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
index 4fe69b9cc..eeb56d998 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.fragments.list.comments
+package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
import androidx.compose.foundation.Image
@@ -25,9 +25,10 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.theme.AppTheme
+import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
@@ -106,7 +107,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
}
Text(
- text = rememberParsedText(comment.commentText),
+ text = rememberParsedDescription(comment.commentText),
style = MaterialTheme.typography.bodyMedium
)
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
similarity index 96%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
rename to app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index 900392c09..72e401d9a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.fragments.list.comments
+package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
@@ -28,14 +28,15 @@ import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.paging.CommentsDisabledException
@Composable
fun CommentSection(
- flow: Flow>,
parentComment: CommentsInfoItem? = null,
+ flow: Flow>
) {
val replies = flow.collectAsLazyPagingItems()
val itemCount by remember { derivedStateOf { replies.itemCount } }
diff --git a/app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt b/app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt
rename to app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt
index b61906ebe..0aa330390 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.theme
+package org.schabi.newpipe.compose.theme
import androidx.compose.ui.graphics.Color
diff --git a/app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt b/app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt
similarity index 88%
rename from app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt
rename to app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt
index d8104d7ae..274fa9d43 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.theme
+package org.schabi.newpipe.compose.theme
import androidx.compose.ui.unit.dp
diff --git a/app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt b/app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt
rename to app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt
index 846794d72..1c9325f96 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.theme
+package org.schabi.newpipe.compose.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
diff --git a/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt b/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
new file mode 100644
index 000000000..567a106ed
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
@@ -0,0 +1,20 @@
+package org.schabi.newpipe.compose.util
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ParagraphStyle
+import androidx.compose.ui.text.fromHtml
+import org.schabi.newpipe.extractor.stream.Description
+
+@Composable
+fun rememberParsedDescription(description: Description): AnnotatedString {
+ // TODO: Handle links and hashtags, Markdown.
+ return remember(description) {
+ if (description.type == Description.HTML) {
+ AnnotatedString.fromHtml(description.content)
+ } else {
+ AnnotatedString(description.content, ParagraphStyle())
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index decd9391c..76eb0041e 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -11,7 +11,9 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.paging.Pager
import androidx.paging.PagingConfig
-import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.compose.comment.CommentSection
+import org.schabi.newpipe.compose.theme.AppTheme
+import org.schabi.newpipe.paging.CommentsSource
import org.schabi.newpipe.util.NO_SERVICE_ID
class CommentsFragment : Fragment() {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
similarity index 96%
rename from app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
rename to app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
index 9e46a9df1..d78a7a83d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsSource.kt
+++ b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.fragments.list.comments
+package org.schabi.newpipe.paging
import androidx.paging.PagingState
import androidx.paging.rxjava3.RxPagingSource
From 02c5f2607a65e9f7d7509ad5dc278a28c1d42bd2 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 30 Jun 2024 21:34:42 +0530
Subject: [PATCH 35/93] Cache paging data using the cachedIn() extension
---
app/build.gradle | 2 +-
.../schabi/newpipe/compose/comment/Comment.kt | 8 +++-
.../newpipe/compose/comment/CommentSection.kt | 14 +++----
.../list/comments/CommentsFragment.kt | 8 +++-
.../schabi/newpipe/paging/CommentsSource.kt | 39 +++++++++++--------
.../schabi/newpipe/util/ExtractorHelper.java | 20 ----------
6 files changed, 43 insertions(+), 48 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7bea96099..5929582b0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -293,7 +293,7 @@ dependencies {
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
- implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString
+ implementation 'androidx.compose.ui:ui-text:1.7.0-beta04' // Needed for parsing HTML to AnnotatedString
implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
// Paging
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index b63845ae1..97dce39a5 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -37,6 +38,7 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import androidx.paging.Pager
import androidx.paging.PagingConfig
+import androidx.paging.cachedIn
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.compose.theme.AppTheme
@@ -141,13 +143,15 @@ fun Comment(comment: CommentsInfoItem) {
if (showReplies) {
ModalBottomSheet(onDismissRequest = { showReplies = false }) {
- val flow = remember(comment) {
+ val coroutineScope = rememberCoroutineScope()
+ val flow = remember(coroutineScope) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
CommentsSource(comment.serviceId, comment.url, comment.replies)
}.flow
+ .cachedIn(coroutineScope)
}
- CommentSection(parentComment = comment, flow = flow)
+ CommentSection(parentComment = comment, commentsData = flow)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index 72e401d9a..df2c470a9 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -36,13 +36,13 @@ import org.schabi.newpipe.paging.CommentsDisabledException
@Composable
fun CommentSection(
parentComment: CommentsInfoItem? = null,
- flow: Flow>
+ commentsData: Flow>
) {
- val replies = flow.collectAsLazyPagingItems()
- val itemCount by remember { derivedStateOf { replies.itemCount } }
+ val comments = commentsData.collectAsLazyPagingItems()
+ val itemCount by remember { derivedStateOf { comments.itemCount } }
Surface(color = MaterialTheme.colorScheme.background) {
- val refresh = replies.loadState.refresh
+ val refresh = comments.loadState.refresh
if (itemCount == 0 && refresh !is LoadState.Loading) {
NoCommentsMessage((refresh as? LoadState.Error)?.error)
} else {
@@ -58,7 +58,7 @@ fun CommentSection(
}
items(itemCount) {
- Comment(comment = replies[it]!!)
+ Comment(comment = comments[it]!!)
}
}
}
@@ -113,7 +113,7 @@ private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData
) {
AppTheme {
- CommentSection(flow = flowOf(pagingData))
+ CommentSection(commentsData = flowOf(pagingData))
}
}
@@ -137,6 +137,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- CommentSection(parentComment = comment, flow = flow)
+ CommentSection(parentComment = comment, commentsData = flow)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 76eb0041e..5933ea809 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -5,12 +5,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.paging.Pager
import androidx.paging.PagingConfig
+import androidx.paging.cachedIn
import org.schabi.newpipe.compose.comment.CommentSection
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.paging.CommentsSource
@@ -29,14 +31,16 @@ class CommentsFragment : Fragment() {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- val flow = remember(serviceId, url) {
+ val coroutineScope = rememberCoroutineScope()
+ val flow = remember(coroutineScope) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
CommentsSource(serviceId, url, null)
}.flow
+ .cachedIn(coroutineScope)
}
AppTheme {
- CommentSection(flow = flow)
+ CommentSection(commentsData = flow)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
index d78a7a83d..aec24a344 100644
--- a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
+++ b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
@@ -1,35 +1,42 @@
package org.schabi.newpipe.paging
+import androidx.paging.PagingSource
import androidx.paging.PagingState
-import androidx.paging.rxjava3.RxPagingSource
-import io.reactivex.rxjava3.core.Single
-import io.reactivex.rxjava3.schedulers.Schedulers
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.Page
+import org.schabi.newpipe.extractor.comments.CommentsInfo
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
-import org.schabi.newpipe.util.ExtractorHelper
+import org.schabi.newpipe.util.NO_SERVICE_ID
class CommentsSource(
- private val serviceId: Int,
+ serviceId: Int,
private val url: String?,
private val repliesPage: Page?
-) : RxPagingSource() {
- override fun loadSingle(params: LoadParams): Single> {
+) : PagingSource() {
+ init {
+ require(serviceId != NO_SERVICE_ID) { "serviceId is NO_SERVICE_ID" }
+ }
+ private val service = NewPipe.getService(serviceId)
+
+ override suspend fun load(params: LoadParams): LoadResult {
// repliesPage is non-null only when used to load the comment replies
val nextKey = params.key ?: repliesPage
- return nextKey?.let {
- ExtractorHelper.getMoreCommentItems(serviceId, url, it)
- .subscribeOn(Schedulers.io())
- .map { LoadResult.Page(it.items, null, it.nextPage) }
- } ?: ExtractorHelper.getCommentsInfo(serviceId, url, false)
- .subscribeOn(Schedulers.io())
- .map {
- if (it.isCommentsDisabled) {
+ return withContext(Dispatchers.IO) {
+ nextKey?.let {
+ val info = CommentsInfo.getMoreItems(service, url, it)
+ LoadResult.Page(info.items, null, info.nextPage)
+ } ?: run {
+ val info = CommentsInfo.getInfo(service, url)
+ if (info.isCommentsDisabled) {
LoadResult.Error(CommentsDisabledException())
} else {
- LoadResult.Page(it.relatedItems, null, it.nextPage)
+ LoadResult.Page(info.relatedItems, null, info.nextPage)
}
}
+ }
}
override fun getRefreshKey(state: PagingState) = null
diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
index abf8d24c1..83f2332ed 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
@@ -42,8 +42,6 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo;
-import org.schabi.newpipe.extractor.comments.CommentsInfo;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
@@ -146,24 +144,6 @@ public final class ExtractorHelper {
listLinkHandler, nextPage));
}
- public static Single getCommentsInfo(final int serviceId,
- final String url,
- final boolean forceLoad) {
- checkServiceId(serviceId);
- return checkCache(forceLoad, serviceId, url, InfoCache.Type.COMMENTS,
- Single.fromCallable(() ->
- CommentsInfo.getInfo(NewPipe.getService(serviceId), url)));
- }
-
- public static Single> getMoreCommentItems(
- final int serviceId,
- final String url,
- final Page nextPage) {
- checkServiceId(serviceId);
- return Single.fromCallable(() ->
- CommentsInfo.getMoreItems(NewPipe.getService(serviceId), url, nextPage));
- }
-
public static Single getPlaylistInfo(final int serviceId,
final String url,
final boolean forceLoad) {
From c5d94a5b60b16b9026b957a0a4e1732ab2e37219 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 2 Jul 2024 18:19:17 +0530
Subject: [PATCH 36/93] Add comment view model
---
app/build.gradle | 6 +--
.../schabi/newpipe/compose/comment/Comment.kt | 4 +-
.../newpipe/compose/comment/CommentSection.kt | 8 ++--
.../list/comments/CommentsFragment.kt | 45 +++++--------------
.../newpipe/viewmodels/CommentsViewModel.kt | 22 +++++++++
5 files changed, 41 insertions(+), 44 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
diff --git a/app/build.gradle b/app/build.gradle
index 5929582b0..ed85f281a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -294,11 +294,9 @@ dependencies {
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta04' // Needed for parsing HTML to AnnotatedString
- implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
-
- // Paging
- implementation 'androidx.paging:paging-rxjava3:3.3.0'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
implementation 'androidx.paging:paging-compose:3.3.0'
+ implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
/** Debugging **/
// Memory leak detection
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index 97dce39a5..b372edff9 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -144,14 +144,14 @@ fun Comment(comment: CommentsInfoItem) {
if (showReplies) {
ModalBottomSheet(onDismissRequest = { showReplies = false }) {
val coroutineScope = rememberCoroutineScope()
- val flow = remember(coroutineScope) {
+ val flow = remember {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
CommentsSource(comment.serviceId, comment.url, comment.replies)
}.flow
.cachedIn(coroutineScope)
}
- CommentSection(parentComment = comment, commentsData = flow)
+ CommentSection(parentComment = comment, commentsFlow = flow)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index df2c470a9..8d1ef90ab 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -36,9 +36,9 @@ import org.schabi.newpipe.paging.CommentsDisabledException
@Composable
fun CommentSection(
parentComment: CommentsInfoItem? = null,
- commentsData: Flow>
+ commentsFlow: Flow>
) {
- val comments = commentsData.collectAsLazyPagingItems()
+ val comments = commentsFlow.collectAsLazyPagingItems()
val itemCount by remember { derivedStateOf { comments.itemCount } }
Surface(color = MaterialTheme.colorScheme.background) {
@@ -113,7 +113,7 @@ private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData
) {
AppTheme {
- CommentSection(commentsData = flowOf(pagingData))
+ CommentSection(commentsFlow = flowOf(pagingData))
}
}
@@ -137,6 +137,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- CommentSection(parentComment = comment, commentsData = flow)
+ CommentSection(parentComment = comment, commentsFlow = flow)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 5933ea809..22cff9a53 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -2,60 +2,37 @@ package org.schabi.newpipe.fragments.list.comments
import android.os.Bundle
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import androidx.paging.cachedIn
+import androidx.lifecycle.viewmodel.compose.viewModel
import org.schabi.newpipe.compose.comment.CommentSection
import org.schabi.newpipe.compose.theme.AppTheme
-import org.schabi.newpipe.paging.CommentsSource
-import org.schabi.newpipe.util.NO_SERVICE_ID
+import org.schabi.newpipe.util.KEY_SERVICE_ID
+import org.schabi.newpipe.util.KEY_URL
+import org.schabi.newpipe.viewmodels.CommentsViewModel
class CommentsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View {
- val arguments = requireArguments()
- val serviceId = arguments.getInt(SERVICE_ID, NO_SERVICE_ID)
- val url = arguments.getString(URL)
-
- return ComposeView(requireContext()).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- val coroutineScope = rememberCoroutineScope()
- val flow = remember(coroutineScope) {
- Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentsSource(serviceId, url, null)
- }.flow
- .cachedIn(coroutineScope)
- }
-
- AppTheme {
- CommentSection(commentsData = flow)
- }
+ ) = ComposeView(requireContext()).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ val viewModel = viewModel()
+ AppTheme {
+ CommentSection(commentsFlow = viewModel.comments)
}
}
}
companion object {
- private const val SERVICE_ID = "serviceId"
- private const val URL = "url"
-
@JvmStatic
fun getInstance(serviceId: Int, url: String?) = CommentsFragment().apply {
- arguments = bundleOf(
- SERVICE_ID to serviceId,
- URL to url
- )
+ arguments = bundleOf(KEY_SERVICE_ID to serviceId, KEY_URL to url)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt b/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
new file mode 100644
index 000000000..62babb186
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
@@ -0,0 +1,22 @@
+package org.schabi.newpipe.viewmodels
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.cachedIn
+import org.schabi.newpipe.paging.CommentsSource
+import org.schabi.newpipe.util.KEY_SERVICE_ID
+import org.schabi.newpipe.util.KEY_URL
+import org.schabi.newpipe.util.NO_SERVICE_ID
+
+class CommentsViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
+ private val serviceId = savedStateHandle[KEY_SERVICE_ID] ?: NO_SERVICE_ID
+ private val url = savedStateHandle.get(KEY_URL)
+
+ val comments = Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentsSource(serviceId, url, null)
+ }.flow
+ .cachedIn(viewModelScope)
+}
From e72da94eb191a727de32c9bc3bbcba3d969cc1b9 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 5 Jul 2024 16:00:46 +0530
Subject: [PATCH 37/93] Rm extra padding in header
---
.../org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
index eeb56d998..a4939cfc2 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
@@ -45,7 +45,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
) {
Row(
modifier = Modifier
- .padding(top = 8.dp, bottom = 8.dp, end = 8.dp)
+ .padding(bottom = 8.dp, end = 8.dp)
.clickable {
NavigationHelper.openCommentAuthorIfPresent(
context as FragmentActivity,
From 42cb914616fb12708b6ed31cc9f34daaaed56cf8 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 5 Jul 2024 16:14:36 +0530
Subject: [PATCH 38/93] Replace padding modifier with verticalArrangement in
comment header
---
.../compose/comment/CommentRepliesHeader.kt | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
index a4939cfc2..52a360c0c 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
@@ -37,21 +37,17 @@ import org.schabi.newpipe.util.image.ImageStrategy
fun CommentRepliesHeader(comment: CommentsInfoItem) {
val context = LocalContext.current
- Column(modifier = Modifier.padding(all = 8.dp)) {
+ Column(modifier = Modifier.padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
- modifier = Modifier
- .padding(bottom = 8.dp, end = 8.dp)
- .clickable {
- NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity,
- comment
- )
- },
+ modifier = Modifier.clickable {
+ val activity = context as FragmentActivity
+ NavigationHelper.openCommentAuthorIfPresent(activity, comment)
+ },
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
From 1009dc4d4e853b9f633c0c7bec98535f201d4adb Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 7 Jul 2024 04:58:35 +0530
Subject: [PATCH 39/93] Added loading indicator
---
.../newpipe/compose/comment/CommentSection.kt | 41 ++++++++++---------
.../compose/status/LoadingIndicator.kt | 18 ++++++++
2 files changed, 40 insertions(+), 19 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index 8d1ef90ab..a26bab8b7 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -2,6 +2,7 @@ package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
@@ -9,10 +10,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -26,8 +25,8 @@ import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
-import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.status.LoadingIndicator
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
@@ -38,26 +37,30 @@ fun CommentSection(
parentComment: CommentsInfoItem? = null,
commentsFlow: Flow>
) {
- val comments = commentsFlow.collectAsLazyPagingItems()
- val itemCount by remember { derivedStateOf { comments.itemCount } }
-
Surface(color = MaterialTheme.colorScheme.background) {
+ val comments = commentsFlow.collectAsLazyPagingItems()
val refresh = comments.loadState.refresh
- if (itemCount == 0 && refresh !is LoadState.Loading) {
- NoCommentsMessage((refresh as? LoadState.Error)?.error)
- } else {
- val listState = rememberLazyListState()
+ val listState = rememberLazyListState()
- LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
- LazyColumn(state = listState) {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
+ LazyColumnScrollbar(state = listState) {
+ LazyColumn(state = listState) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
+ }
+
+ if (comments.itemCount == 0) {
+ item {
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ NoCommentsMessage((refresh as? LoadState.Error)?.error)
}
}
-
- items(itemCount) {
+ } else {
+ items(comments.itemCount) {
Comment(comment = comments[it]!!)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt b/app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt
new file mode 100644
index 000000000..8bed6f8c8
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt
@@ -0,0 +1,18 @@
+package org.schabi.newpipe.compose.status
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+
+@Composable
+fun LoadingIndicator(modifier: Modifier = Modifier) {
+ CircularProgressIndicator(
+ modifier = modifier.fillMaxSize().wrapContentSize(Alignment.Center),
+ color = MaterialTheme.colorScheme.primary,
+ trackColor = MaterialTheme.colorScheme.surfaceVariant,
+ )
+}
From d131d3399a580a60934c9a43d5294c5cab480db2 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Mon, 8 Jul 2024 16:21:34 +0530
Subject: [PATCH 40/93] Rm unused method
---
app/src/main/java/org/schabi/newpipe/util/Localization.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java
index bc113e8f8..097097d89 100644
--- a/app/src/main/java/org/schabi/newpipe/util/Localization.java
+++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java
@@ -219,11 +219,6 @@ public final class Localization {
deletedCount, shortCount(context, deletedCount));
}
- public static String replyCount(@NonNull final Context context, final int replyCount) {
- return getQuantity(context, R.plurals.replies, 0, replyCount,
- String.valueOf(replyCount));
- }
-
/**
* @param context the Android context
* @param likeCount the like count, possibly negative if unknown
From ac1ca1412d7839e060df23faea0321fde624f39c Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Mon, 8 Jul 2024 19:27:57 +0530
Subject: [PATCH 41/93] Improve comment loading smoothness
---
.../schabi/newpipe/compose/comment/CommentSection.kt | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index a26bab8b7..1e32e14dd 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -10,6 +10,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -39,7 +42,7 @@ fun CommentSection(
) {
Surface(color = MaterialTheme.colorScheme.background) {
val comments = commentsFlow.collectAsLazyPagingItems()
- val refresh = comments.loadState.refresh
+ val itemCount by remember { derivedStateOf { comments.itemCount } }
val listState = rememberLazyListState()
LazyColumnScrollbar(state = listState) {
@@ -51,8 +54,9 @@ fun CommentSection(
}
}
- if (comments.itemCount == 0) {
+ if (itemCount == 0) {
item {
+ val refresh = comments.loadState.refresh
if (refresh is LoadState.Loading) {
LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
} else {
@@ -60,7 +64,7 @@ fun CommentSection(
}
}
} else {
- items(comments.itemCount) {
+ items(itemCount) {
Comment(comment = comments[it]!!)
}
}
From e639b02fed7a7898c3798155ce79d274d56b7bff Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 9 Jul 2024 21:45:18 +0530
Subject: [PATCH 42/93] Animate comment expand/collapse
---
app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index b372edff9..a1ec62a24 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -1,6 +1,7 @@
package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
+import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -101,6 +102,7 @@ fun Comment(comment: CommentsInfoItem) {
}
Text(
+ modifier = Modifier.animateContentSize(),
text = rememberParsedDescription(comment.commentText),
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
From 4740e3be86ee6b9be3797c901254b562492d9df3 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 10 Jul 2024 15:42:10 +0530
Subject: [PATCH 43/93] Make parsed links clickable, visible
---
app/build.gradle | 2 +-
.../org/schabi/newpipe/compose/util/ParseDescription.kt | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index ed85f281a..c516f8ce2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -290,7 +290,7 @@ dependencies {
// Jetpack Compose
implementation(platform('androidx.compose:compose-bom:2024.06.00'))
- implementation 'androidx.compose.material3:material3'
+ implementation 'androidx.compose.material3:material3:1.3.0-beta04'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta04' // Needed for parsing HTML to AnnotatedString
diff --git a/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt b/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
index 567a106ed..3ffbac558 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
@@ -4,7 +4,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.fromHtml
+import androidx.compose.ui.text.style.TextDecoration
import org.schabi.newpipe.extractor.stream.Description
@Composable
@@ -12,7 +15,8 @@ fun rememberParsedDescription(description: Description): AnnotatedString {
// TODO: Handle links and hashtags, Markdown.
return remember(description) {
if (description.type == Description.HTML) {
- AnnotatedString.fromHtml(description.content)
+ val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
+ AnnotatedString.fromHtml(description.content, styles)
} else {
AnnotatedString(description.content, ParagraphStyle())
}
From edab9a6a1f0c512aa0fe112f87e73d7426c3a61b Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 12 Jul 2024 07:11:04 +0530
Subject: [PATCH 44/93] Fix alignment of comment message
---
.../org/schabi/newpipe/compose/comment/CommentSection.kt | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index 1e32e14dd..cf72ad7f4 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -2,7 +2,9 @@ package org.schabi.newpipe.compose.comment
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
@@ -81,7 +83,10 @@ private fun NoCommentsMessage(error: Throwable?) {
R.string.no_comments
}
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Column(
+ modifier = Modifier.fillMaxWidth().wrapContentSize(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
Text(text = "(╯°-°)╯", fontSize = 35.sp)
Text(text = stringResource(id = message), fontSize = 24.sp)
}
From f984b266260e22173b9db624b610f7fa4960c1df Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 16 Jul 2024 19:09:38 +0530
Subject: [PATCH 45/93] Fix some modifiers
---
.../main/java/org/schabi/newpipe/compose/comment/Comment.kt | 5 ++---
.../org/schabi/newpipe/compose/comment/CommentSection.kt | 4 +++-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index a1ec62a24..a5bf00da5 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -61,9 +61,9 @@ fun Comment(comment: CommentsInfoItem) {
Row(
modifier = Modifier
- .fillMaxWidth()
+ .animateContentSize()
.clickable { isExpanded = !isExpanded }
- .padding(all = 8.dp),
+ .padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
if (ImageStrategy.shouldLoadImages()) {
@@ -102,7 +102,6 @@ fun Comment(comment: CommentsInfoItem) {
}
Text(
- modifier = Modifier.animateContentSize(),
text = rememberParsedDescription(comment.commentText),
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index cf72ad7f4..7a9579fb1 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -84,7 +84,9 @@ private fun NoCommentsMessage(error: Throwable?) {
}
Column(
- modifier = Modifier.fillMaxWidth().wrapContentSize(Alignment.Center),
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "(╯°-°)╯", fontSize = 35.sp)
From ea414f57d46fb41f47b4e717bb35ea68639903ce Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Thu, 25 Jul 2024 15:38:57 +0530
Subject: [PATCH 46/93] Added DescriptionText composable
---
.../schabi/newpipe/compose/comment/Comment.kt | 6 ++---
.../compose/comment/CommentRepliesHeader.kt | 6 ++---
.../newpipe/compose/comment/CommentSection.kt | 2 +-
.../DescriptionText.kt} | 25 ++++++++++++++++---
.../{status => common}/LoadingIndicator.kt | 2 +-
5 files changed, 30 insertions(+), 11 deletions(-)
rename app/src/main/java/org/schabi/newpipe/compose/{util/ParseDescription.kt => common/DescriptionText.kt} (53%)
rename app/src/main/java/org/schabi/newpipe/compose/{status => common}/LoadingIndicator.kt (93%)
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
index a5bf00da5..00f07932f 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
@@ -42,8 +42,8 @@ import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import coil.compose.AsyncImage
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
-import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
@@ -101,8 +101,8 @@ fun Comment(comment: CommentsInfoItem) {
Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
}
- Text(
- text = rememberParsedDescription(comment.commentText),
+ DescriptionText(
+ description = comment.commentText,
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
index 52a360c0c..d4e4fd36c 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
@@ -25,8 +25,8 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
+import org.schabi.newpipe.compose.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
-import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.util.Localization
@@ -102,8 +102,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
}
}
- Text(
- text = rememberParsedDescription(comment.commentText),
+ DescriptionText(
+ description = comment.commentText,
style = MaterialTheme.typography.bodyMedium
)
}
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
index 7a9579fb1..5d5ad3b76 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
@@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import org.schabi.newpipe.R
-import org.schabi.newpipe.compose.status.LoadingIndicator
+import org.schabi.newpipe.compose.common.LoadingIndicator
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
diff --git a/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt b/app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt
similarity index 53%
rename from app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
rename to app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt
index 3ffbac558..614a36cce 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/util/ParseDescription.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt
@@ -1,19 +1,30 @@
-package org.schabi.newpipe.compose.util
+package org.schabi.newpipe.compose.common
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
import org.schabi.newpipe.extractor.stream.Description
@Composable
-fun rememberParsedDescription(description: Description): AnnotatedString {
+fun DescriptionText(
+ description: Description,
+ modifier: Modifier = Modifier,
+ overflow: TextOverflow = TextOverflow.Clip,
+ maxLines: Int = Int.MAX_VALUE,
+ style: TextStyle = LocalTextStyle.current
+) {
// TODO: Handle links and hashtags, Markdown.
- return remember(description) {
+ val parsedDescription = remember(description) {
if (description.type == Description.HTML) {
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
AnnotatedString.fromHtml(description.content, styles)
@@ -21,4 +32,12 @@ fun rememberParsedDescription(description: Description): AnnotatedString {
AnnotatedString(description.content, ParagraphStyle())
}
}
+
+ Text(
+ modifier = modifier,
+ text = parsedDescription,
+ maxLines = maxLines,
+ style = style,
+ overflow = overflow
+ )
}
diff --git a/app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt b/app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt
similarity index 93%
rename from app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt
rename to app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt
index 8bed6f8c8..6ddea9d7a 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/status/LoadingIndicator.kt
+++ b/app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.status
+package org.schabi.newpipe.compose.common
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
From eaac7f3f858d1546ee777bec62ec7be931a65c0f Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 28 Jul 2024 06:50:45 +0530
Subject: [PATCH 47/93] Improved component organisation
---
.../newpipe/fragments/list/comments/CommentsFragment.kt | 4 ++--
.../main/java/org/schabi/newpipe/{compose => ui}/Toolbar.kt | 6 +++---
.../newpipe/{compose => ui/components}/comment/Comment.kt | 6 +++---
.../components}/comment/CommentRepliesHeader.kt | 6 +++---
.../{compose => ui/components}/comment/CommentSection.kt | 6 +++---
.../{compose => ui/components}/common/DescriptionText.kt | 2 +-
.../{compose => ui/components}/common/LoadingIndicator.kt | 2 +-
.../java/org/schabi/newpipe/{compose => ui}/theme/Color.kt | 2 +-
.../org/schabi/newpipe/{compose => ui}/theme/SizeTokens.kt | 2 +-
.../java/org/schabi/newpipe/{compose => ui}/theme/Theme.kt | 2 +-
10 files changed, 19 insertions(+), 19 deletions(-)
rename app/src/main/java/org/schabi/newpipe/{compose => ui}/Toolbar.kt (97%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui/components}/comment/Comment.kt (98%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui/components}/comment/CommentRepliesHeader.kt (96%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui/components}/comment/CommentSection.kt (97%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui/components}/common/DescriptionText.kt (96%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui/components}/common/LoadingIndicator.kt (92%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui}/theme/Color.kt (98%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui}/theme/SizeTokens.kt (88%)
rename app/src/main/java/org/schabi/newpipe/{compose => ui}/theme/Theme.kt (98%)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 22cff9a53..10eea4e78 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -8,8 +8,8 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.viewmodel.compose.viewModel
-import org.schabi.newpipe.compose.comment.CommentSection
-import org.schabi.newpipe.compose.theme.AppTheme
+import org.schabi.newpipe.ui.components.comment.CommentSection
+import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
import org.schabi.newpipe.viewmodels.CommentsViewModel
diff --git a/app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt b/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
similarity index 97%
rename from app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt
rename to app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
index 469d88ec0..b788932a2 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/Toolbar.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/Toolbar.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose
+package org.schabi.newpipe.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -27,8 +27,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import org.schabi.newpipe.R
-import org.schabi.newpipe.compose.theme.AppTheme
-import org.schabi.newpipe.compose.theme.SizeTokens
+import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.ui.theme.SizeTokens
@Composable
fun TextAction(text: String, modifier: Modifier = Modifier) {
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
index 00f07932f..8ecc87fdd 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.comment
+package org.schabi.newpipe.ui.components.comment
import android.content.res.Configuration
import androidx.compose.animation.animateContentSize
@@ -42,12 +42,12 @@ import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import coil.compose.AsyncImage
import org.schabi.newpipe.R
-import org.schabi.newpipe.compose.common.DescriptionText
-import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentsSource
+import org.schabi.newpipe.ui.components.common.DescriptionText
+import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
similarity index 96%
rename from app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
index d4e4fd36c..a8e33a49d 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.comment
+package org.schabi.newpipe.ui.components.comment
import android.content.res.Configuration
import androidx.compose.foundation.Image
@@ -25,10 +25,10 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
-import org.schabi.newpipe.compose.common.DescriptionText
-import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.ui.components.common.DescriptionText
+import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
diff --git a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
similarity index 97%
rename from app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
index 5d5ad3b76..3f2a5a1ac 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.comment
+package org.schabi.newpipe.ui.components.comment
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
@@ -31,11 +31,11 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import org.schabi.newpipe.R
-import org.schabi.newpipe.compose.common.LoadingIndicator
-import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentsDisabledException
+import org.schabi.newpipe.ui.components.common.LoadingIndicator
+import org.schabi.newpipe.ui.theme.AppTheme
@Composable
fun CommentSection(
diff --git a/app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
similarity index 96%
rename from app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
index 614a36cce..59ce07c94 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/common/DescriptionText.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.common
+package org.schabi.newpipe.ui.components.common
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
diff --git a/app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/LoadingIndicator.kt
similarity index 92%
rename from app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/common/LoadingIndicator.kt
index 6ddea9d7a..3bfe1dee4 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/common/LoadingIndicator.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/LoadingIndicator.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.common
+package org.schabi.newpipe.ui.components.common
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
diff --git a/app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt b/app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt
rename to app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt
index 0aa330390..b61906ebe 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/theme/Color.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/theme/Color.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.theme
+package org.schabi.newpipe.ui.theme
import androidx.compose.ui.graphics.Color
diff --git a/app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt b/app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt
similarity index 88%
rename from app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt
rename to app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt
index 274fa9d43..d8104d7ae 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/theme/SizeTokens.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/theme/SizeTokens.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.theme
+package org.schabi.newpipe.ui.theme
import androidx.compose.ui.unit.dp
diff --git a/app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt b/app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt
rename to app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt
index 1c9325f96..846794d72 100644
--- a/app/src/main/java/org/schabi/newpipe/compose/theme/Theme.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/theme/Theme.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.compose.theme
+package org.schabi.newpipe.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
From e955beeef15515fc04b21c91c87e5d754d8c46ea Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 28 Jul 2024 08:12:19 +0530
Subject: [PATCH 48/93] Update Kotlin to 2.0, update dependencies and fix
issues
---
app/build.gradle | 13 ++++------
.../newpipe/error/ReCaptchaActivity.java | 6 ++---
.../schabi/newpipe/local/feed/FeedFragment.kt | 5 +++-
.../subscription/SubscriptionFragment.kt | 1 +
.../subscription/dialog/FeedGroupDialog.kt | 1 +
.../settings/DownloadSettingsFragment.java | 25 ++++---------------
.../NotificationModeConfigFragment.kt | 2 ++
build.gradle | 2 +-
8 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c516f8ce2..09cb1f5eb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,6 +9,7 @@ plugins {
id "kotlin-parcelize"
id "checkstyle"
id "org.sonarqube" version "4.0.0.2929"
+ id "org.jetbrains.kotlin.plugin.compose" version "${kotlin_version}"
}
android {
@@ -104,10 +105,6 @@ android {
'META-INF/COPYRIGHT']
}
}
-
- composeOptions {
- kotlinCompilerExtensionVersion = "1.5.14"
- }
}
ext {
@@ -290,13 +287,13 @@ dependencies {
// Jetpack Compose
implementation(platform('androidx.compose:compose-bom:2024.06.00'))
- implementation 'androidx.compose.material3:material3:1.3.0-beta04'
+ implementation 'androidx.compose.material3:material3:1.3.0-beta05'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
- implementation 'androidx.compose.ui:ui-text:1.7.0-beta04' // Needed for parsing HTML to AnnotatedString
+ implementation 'androidx.compose.ui:ui-text:1.7.0-beta06' // Needed for parsing HTML to AnnotatedString
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
- implementation 'androidx.paging:paging-compose:3.3.0'
- implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
+ implementation 'androidx.paging:paging-compose:3.3.1'
+ implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'
/** Debugging **/
// Memory leak detection
diff --git a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
index 42ef261a1..51a0ff1e6 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java
@@ -185,10 +185,8 @@ public class ReCaptchaActivity extends AppCompatActivity {
final int abuseEnd = url.indexOf("+path");
try {
- String abuseCookie = url.substring(abuseStart + 13, abuseEnd);
- abuseCookie = Utils.decodeUrlUtf8(abuseCookie);
- handleCookies(abuseCookie);
- } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) {
+ handleCookies(Utils.decodeUrlUtf8(url.substring(abuseStart + 13, abuseEnd)));
+ } catch (final StringIndexOutOfBoundsException e) {
if (MainActivity.DEBUG) {
Log.e(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
+ abuseStart + " and ending at " + abuseEnd + " for url " + url, e);
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
index e8c5b1e34..b99291309 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
@@ -202,6 +202,7 @@ class FeedFragment : BaseStateFragment() {
// Menu
// /////////////////////////////////////////////////////////////////////////
+ @Deprecated("Deprecated in Java")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
@@ -212,6 +213,7 @@ class FeedFragment : BaseStateFragment() {
inflater.inflate(R.menu.menu_feed_fragment, menu)
}
+ @Deprecated("Deprecated in Java")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.menu_item_feed_help) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
@@ -253,7 +255,7 @@ class FeedFragment : BaseStateFragment() {
viewModel.getShowFutureItemsFromPreferences()
)
- AlertDialog.Builder(context!!)
+ AlertDialog.Builder(requireContext())
.setTitle(R.string.feed_hide_streams_title)
.setMultiChoiceItems(dialogItems, checkedDialogItems) { _, which, isChecked ->
checkedDialogItems[which] = isChecked
@@ -267,6 +269,7 @@ class FeedFragment : BaseStateFragment() {
.show()
}
+ @Deprecated("Deprecated in Java")
override fun onDestroyOptionsMenu() {
super.onDestroyOptionsMenu()
activity?.supportActionBar?.subtitle = null
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
index fe2321059..59bbaee9d 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt
@@ -129,6 +129,7 @@ class SubscriptionFragment : BaseStateFragment() {
// Menu
// ////////////////////////////////////////////////////////////////////////
+ @Deprecated("Deprecated in Java")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
index 41761fb01..954b872a6 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt
@@ -94,6 +94,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireActivity(), theme) {
+ @Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (!this@FeedGroupDialog.onBackPressed()) {
super.onBackPressed()
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
index 76163b30a..ff7811af3 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java
@@ -1,6 +1,5 @@
package org.schabi.newpipe.settings;
-import static org.schabi.newpipe.extractor.utils.Utils.decodeUrlUtf8;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.app.Activity;
@@ -30,7 +29,6 @@ import org.schabi.newpipe.util.FilePickerActivityHelper;
import java.io.File;
import java.io.IOException;
-import java.net.URI;
public class DownloadSettingsFragment extends BasePreferenceFragment {
public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true;
@@ -107,28 +105,15 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
private void showPathInSummary(final String prefKey, @StringRes final int defaultString,
final Preference target) {
- String rawUri = defaultPreferences.getString(prefKey, null);
- if (rawUri == null || rawUri.isEmpty()) {
+ final Uri uri = Uri.parse(defaultPreferences.getString(prefKey, ""));
+ if (uri.equals(Uri.EMPTY)) {
target.setSummary(getString(defaultString));
return;
}
- if (rawUri.charAt(0) == File.separatorChar) {
- target.setSummary(rawUri);
- return;
- }
- if (rawUri.startsWith(ContentResolver.SCHEME_FILE)) {
- target.setSummary(new File(URI.create(rawUri)).getPath());
- return;
- }
-
- try {
- rawUri = decodeUrlUtf8(rawUri);
- } catch (final IllegalArgumentException e) {
- // nothing to do
- }
-
- target.setSummary(rawUri);
+ final String summary = ContentResolver.SCHEME_FILE.equals(uri.getScheme())
+ ? uri.getPath() : uri.toString();
+ target.setSummary(summary);
}
private boolean isFileUri(final String path) {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
index 581768c30..2df3e33b6 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/settings/notifications/NotificationModeConfigFragment.kt
@@ -77,11 +77,13 @@ class NotificationModeConfigFragment : Fragment() {
super.onDestroy()
}
+ @Deprecated("Deprecated in Java")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_notifications_channels, menu)
}
+ @Deprecated("Deprecated in Java")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_toggle_all -> {
diff --git a/build.gradle b/build.gradle
index 49de98659..1acfb6f4a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.9.24'
+ ext.kotlin_version = '2.0.0'
repositories {
google()
mavenCentral()
From f9dae9078e4d091906f1e82ced9acf5b15045379 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sun, 4 Aug 2024 05:07:55 +0530
Subject: [PATCH 49/93] Always show comment thumbnails, even if placeholders
---
.../newpipe/ui/components/comment/Comment.kt | 30 +++++++++----------
.../comment/CommentRepliesHeader.kt | 20 ++++++-------
2 files changed, 23 insertions(+), 27 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
index 8ecc87fdd..d01f3550c 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
@@ -66,22 +66,20 @@ fun Comment(comment: CommentsInfoItem) {
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
- if (ImageStrategy.shouldLoadImages()) {
- AsyncImage(
- model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
- contentDescription = null,
- placeholder = painterResource(R.drawable.placeholder_person),
- error = painterResource(R.drawable.placeholder_person),
- modifier = Modifier
- .size(42.dp)
- .clip(CircleShape)
- .clickable {
- NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity, comment
- )
- }
- )
- }
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(
+ context as FragmentActivity, comment
+ )
+ }
+ )
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
index a8e33a49d..920300f2c 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
@@ -51,17 +51,15 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
- if (ImageStrategy.shouldLoadImages()) {
- AsyncImage(
- model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
- contentDescription = null,
- placeholder = painterResource(R.drawable.placeholder_person),
- error = painterResource(R.drawable.placeholder_person),
- modifier = Modifier
- .size(42.dp)
- .clip(CircleShape)
- )
- }
+ AsyncImage(
+ model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
+ contentDescription = null,
+ placeholder = painterResource(R.drawable.placeholder_person),
+ error = painterResource(R.drawable.placeholder_person),
+ modifier = Modifier
+ .size(42.dp)
+ .clip(CircleShape)
+ )
Column {
Text(text = comment.uploaderName)
From e082bca5e07dea222aaa8976347b747319a3da84 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sat, 10 Aug 2024 06:14:36 +0530
Subject: [PATCH 50/93] Use nested scroll modifier
---
.../newpipe/ui/components/comment/CommentSection.kt | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
index 3f2a5a1ac..d41fb706a 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
@@ -17,6 +17,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -45,10 +47,11 @@ fun CommentSection(
Surface(color = MaterialTheme.colorScheme.background) {
val comments = commentsFlow.collectAsLazyPagingItems()
val itemCount by remember { derivedStateOf { comments.itemCount } }
- val listState = rememberLazyListState()
+ val nestedScrollInterop = rememberNestedScrollInteropConnection()
+ val state = rememberLazyListState()
- LazyColumnScrollbar(state = listState) {
- LazyColumn(state = listState) {
+ LazyColumnScrollbar(state = state) {
+ LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
if (parentComment != null) {
item {
CommentRepliesHeader(comment = parentComment)
From 38c823a042ee3ca880b15d79095087a89d66f627 Mon Sep 17 00:00:00 2001
From: TwoAi
Date: Sat, 10 Aug 2024 15:42:35 -0400
Subject: [PATCH 51/93] Remove outdated returnActivity test code
returnActivity was removed in 463dd8e
---
.../schabi/newpipe/error/ErrorActivity.java | 22 ------------
.../newpipe/error/ErrorActivityTest.java | 35 -------------------
2 files changed, 57 deletions(-)
delete mode 100644 app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java
diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
index 831a8cc4b..2f607b487 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
@@ -2,7 +2,6 @@ package org.schabi.newpipe.error;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -13,7 +12,6 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -22,7 +20,6 @@ import androidx.core.content.IntentCompat;
import com.grack.nanojson.JsonWriter;
import org.schabi.newpipe.BuildConfig;
-import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ActivityErrorBinding;
import org.schabi.newpipe.util.Localization;
@@ -187,25 +184,6 @@ public class ErrorActivity extends AppCompatActivity {
.collect(Collectors.joining(separator + "\n", separator + "\n", separator));
}
- /**
- * Get the checked activity.
- *
- * @param returnActivity the activity to return to
- * @return the casted return activity or null
- */
- @Nullable
- static Class extends Activity> getReturnActivity(final Class> returnActivity) {
- Class extends Activity> checkedReturnActivity = null;
- if (returnActivity != null) {
- if (Activity.class.isAssignableFrom(returnActivity)) {
- checkedReturnActivity = returnActivity.asSubclass(Activity.class);
- } else {
- checkedReturnActivity = MainActivity.class;
- }
- }
- return checkedReturnActivity;
- }
-
private void buildInfo(final ErrorInfo info) {
String text = "";
diff --git a/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java b/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java
deleted file mode 100644
index f77c7b268..000000000
--- a/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.schabi.newpipe.error;
-
-import android.app.Activity;
-
-import org.junit.Test;
-import org.schabi.newpipe.MainActivity;
-import org.schabi.newpipe.RouterActivity;
-import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-/**
- * Unit tests for {@link ErrorActivity}.
- */
-public class ErrorActivityTest {
- @Test
- public void getReturnActivity() {
- Class extends Activity> returnActivity;
- returnActivity = ErrorActivity.getReturnActivity(MainActivity.class);
- assertEquals(MainActivity.class, returnActivity);
-
- returnActivity = ErrorActivity.getReturnActivity(RouterActivity.class);
- assertEquals(RouterActivity.class, returnActivity);
-
- returnActivity = ErrorActivity.getReturnActivity(null);
- assertNull(returnActivity);
-
- returnActivity = ErrorActivity.getReturnActivity(Integer.class);
- assertEquals(MainActivity.class, returnActivity);
-
- returnActivity = ErrorActivity.getReturnActivity(VideoDetailFragment.class);
- assertEquals(MainActivity.class, returnActivity);
- }
-}
From 294b9cf347da8b357b93b06fd86aea2b1a147629 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Sat, 17 Aug 2024 08:25:39 +0530
Subject: [PATCH 52/93] Rm unused declaration
---
.../main/java/org/schabi/newpipe/ktx/Bundle.kt | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
index e01cf620e..61721d546 100644
--- a/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
+++ b/app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
@@ -1,25 +1,9 @@
package org.schabi.newpipe.ktx
-import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import androidx.core.os.BundleCompat
-import java.io.Serializable
-import kotlin.reflect.safeCast
inline fun Bundle.parcelableArrayList(key: String?): ArrayList? {
return BundleCompat.getParcelableArrayList(this, key, T::class.java)
}
-
-inline fun Bundle.serializable(key: String?): T? {
- return getSerializable(this, key, T::class.java)
-}
-
-fun getSerializable(bundle: Bundle, key: String?, clazz: Class): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
- bundle.getSerializable(key, clazz)
- } else {
- @Suppress("DEPRECATION")
- clazz.kotlin.safeCast(bundle.getSerializable(key))
- }
-}
From d3a6991fd4918b5408eaaeb69a66861c331c1971 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Mon, 26 Aug 2024 19:23:50 +0530
Subject: [PATCH 53/93] Use Fragment.content extension, improve comment
composables
---
app/build.gradle | 2 +-
.../list/comments/CommentsFragment.kt | 17 ++--
.../list/videos/RelatedItemsFragment.kt | 17 +---
.../ui/components/comment/CommentSection.kt | 96 +++++++++----------
4 files changed, 56 insertions(+), 76 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 5f6b8e028..0a2b24565 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -216,7 +216,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
- implementation 'androidx.fragment:fragment-ktx:1.6.2'
+ implementation 'androidx.fragment:fragment-compose:1.8.2'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:${androidxLifecycleVersion}"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${androidxLifecycleVersion}"
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 10eea4e78..1d28e8035 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -3,28 +3,25 @@ package org.schabi.newpipe.fragments.list.comments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.fragment.compose.content
import org.schabi.newpipe.ui.components.comment.CommentSection
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
-import org.schabi.newpipe.viewmodels.CommentsViewModel
class CommentsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ) = ComposeView(requireContext()).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- val viewModel = viewModel()
- AppTheme {
- CommentSection(commentsFlow = viewModel.comments)
+ ) = content {
+ AppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection()
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.kt
index b48347f4f..88ebe28f0 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.kt
@@ -2,14 +2,12 @@ package org.schabi.newpipe.fragments.list.videos
import android.os.Bundle
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
+import androidx.fragment.compose.content
import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.ktx.serializable
import org.schabi.newpipe.ui.components.video.RelatedItems
@@ -21,15 +19,10 @@ class RelatedItemsFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View {
- return ComposeView(requireContext()).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- AppTheme {
- Surface(color = MaterialTheme.colorScheme.background) {
- RelatedItems(requireArguments().serializable(KEY_INFO)!!)
- }
- }
+ ) = content {
+ AppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ RelatedItems(requireArguments().serializable(KEY_INFO)!!)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
index d41fb706a..4bff6f166 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
@@ -1,30 +1,24 @@
package org.schabi.newpipe.ui.components.comment
import android.content.res.Configuration
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.LoadState
import androidx.paging.LoadStates
import androidx.paging.PagingData
@@ -37,66 +31,58 @@ import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentsDisabledException
import org.schabi.newpipe.ui.components.common.LoadingIndicator
+import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.viewmodels.CommentsViewModel
+
+@Composable
+fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
+ CommentSection(commentsFlow = commentsViewModel.comments)
+}
@Composable
fun CommentSection(
parentComment: CommentsInfoItem? = null,
commentsFlow: Flow>
) {
- Surface(color = MaterialTheme.colorScheme.background) {
- val comments = commentsFlow.collectAsLazyPagingItems()
- val itemCount by remember { derivedStateOf { comments.itemCount } }
- val nestedScrollInterop = rememberNestedScrollInteropConnection()
- val state = rememberLazyListState()
+ val comments = commentsFlow.collectAsLazyPagingItems()
+ val itemCount by remember { derivedStateOf { comments.itemCount } }
+ val nestedScrollInterop = rememberNestedScrollInteropConnection()
+ val state = rememberLazyListState()
- LazyColumnScrollbar(state = state) {
- LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
+ LazyColumnScrollbar(state = state) {
+ LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
+ }
+
+ if (itemCount == 0) {
+ item {
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val error = (refresh as? LoadState.Error)?.error
+ val message = if (error is CommentsDisabledException) {
+ R.string.comments_are_disabled
+ } else {
+ R.string.no_comments
+ }
+ NoItemsMessage(message)
}
}
-
- if (itemCount == 0) {
- item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else {
- NoCommentsMessage((refresh as? LoadState.Error)?.error)
- }
- }
- } else {
- items(itemCount) {
- Comment(comment = comments[it]!!)
- }
+ } else {
+ items(itemCount) {
+ Comment(comment = comments[it]!!)
}
}
}
}
}
-@Composable
-private fun NoCommentsMessage(error: Throwable?) {
- val message = if (error is CommentsDisabledException) {
- R.string.comments_are_disabled
- } else {
- R.string.no_comments
- }
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentSize(Alignment.Center),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text(text = "(╯°-°)╯", fontSize = 35.sp)
- Text(text = stringResource(id = message), fontSize = 24.sp)
- }
-}
-
private class CommentDataProvider : PreviewParameterProvider> {
private val notLoading = LoadState.NotLoading(true)
@@ -130,7 +116,9 @@ private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData
) {
AppTheme {
- CommentSection(commentsFlow = flowOf(pagingData))
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(commentsFlow = flowOf(pagingData))
+ }
}
}
@@ -154,6 +142,8 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- CommentSection(parentComment = comment, commentsFlow = flow)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(parentComment = comment, commentsFlow = flow)
+ }
}
}
From f9340ae604ab5e8ec22c0ce512f735fbcd8357c0 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Tue, 27 Aug 2024 06:08:11 +0530
Subject: [PATCH 54/93] Improve compose function organisation
---
.../schabi/newpipe/fragments/list/comments/CommentsFragment.kt | 2 +-
.../schabi/newpipe/ui/components/{ => video}/comment/Comment.kt | 2 +-
.../ui/components/{ => video}/comment/CommentRepliesHeader.kt | 2 +-
.../newpipe/ui/components/{ => video}/comment/CommentSection.kt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
rename app/src/main/java/org/schabi/newpipe/ui/components/{ => video}/comment/Comment.kt (99%)
rename app/src/main/java/org/schabi/newpipe/ui/components/{ => video}/comment/CommentRepliesHeader.kt (98%)
rename app/src/main/java/org/schabi/newpipe/ui/components/{ => video}/comment/CommentSection.kt (99%)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 1d28e8035..6e20e1425 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -8,7 +8,7 @@ import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
-import org.schabi.newpipe.ui.components.comment.CommentSection
+import org.schabi.newpipe.ui.components.video.comment.CommentSection
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
similarity index 99%
rename from app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index d01f3550c..f86c4807c 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.components.comment
+package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
import androidx.compose.animation.animateContentSize
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
similarity index 98%
rename from app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 920300f2c..64d391b08 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.components.comment
+package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
import androidx.compose.foundation.Image
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
similarity index 99%
rename from app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
rename to app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index 4bff6f166..a47848750 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -1,4 +1,4 @@
-package org.schabi.newpipe.ui.components.comment
+package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
import androidx.compose.foundation.layout.padding
From 5fffee2c7db216a28a5c365b620dae2069f35e03 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 28 Aug 2024 17:59:38 +0530
Subject: [PATCH 55/93] Fix text color in bottom sheet
---
.../list/comments/CommentsFragment.kt | 6 +-
.../video/comment/CommentSection.kt | 60 +++++++++----------
2 files changed, 30 insertions(+), 36 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 6e20e1425..2c6898d7a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -3,8 +3,6 @@ package org.schabi.newpipe.fragments.list.comments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
@@ -20,9 +18,7 @@ class CommentsFragment : Fragment() {
savedInstanceState: Bundle?
) = content {
AppTheme {
- Surface(color = MaterialTheme.colorScheme.background) {
- CommentSection()
- }
+ CommentSection()
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index a47848750..ed4f50dd0 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -50,33 +50,35 @@ fun CommentSection(
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val state = rememberLazyListState()
- LazyColumnScrollbar(state = state) {
- LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
- }
- }
-
- if (itemCount == 0) {
- item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else {
- val error = (refresh as? LoadState.Error)?.error
- val message = if (error is CommentsDisabledException) {
- R.string.comments_are_disabled
- } else {
- R.string.no_comments
- }
- NoItemsMessage(message)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ LazyColumnScrollbar(state = state) {
+ LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
}
}
- } else {
- items(itemCount) {
- Comment(comment = comments[it]!!)
+
+ if (itemCount == 0) {
+ item {
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val error = (refresh as? LoadState.Error)?.error
+ val message = if (error is CommentsDisabledException) {
+ R.string.comments_are_disabled
+ } else {
+ R.string.no_comments
+ }
+ NoItemsMessage(message)
+ }
+ }
+ } else {
+ items(itemCount) {
+ Comment(comment = comments[it]!!)
+ }
}
}
}
@@ -116,9 +118,7 @@ private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData
) {
AppTheme {
- Surface(color = MaterialTheme.colorScheme.background) {
- CommentSection(commentsFlow = flowOf(pagingData))
- }
+ CommentSection(commentsFlow = flowOf(pagingData))
}
}
@@ -142,8 +142,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- Surface(color = MaterialTheme.colorScheme.background) {
- CommentSection(parentComment = comment, commentsFlow = flow)
- }
+ CommentSection(parentComment = comment, commentsFlow = flow)
}
}
From b1add13bfd73c706dbb032d114603dda89084d3b Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 28 Aug 2024 18:14:36 +0530
Subject: [PATCH 56/93] Address code review comments
---
.../main/java/org/schabi/newpipe/MainActivity.java | 13 ++++++-------
.../fragments/detail/VideoDetailFragment.java | 14 --------------
.../ui/components/video/comment/CommentSection.kt | 13 ++++++++++++-
3 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 6b95ea8a8..41abd10c1 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -553,28 +553,27 @@ public class MainActivity extends AppCompatActivity {
// In case bottomSheet is not visible on the screen or collapsed we can assume that the user
// interacts with a fragment inside fragment_holder so all back presses should be
// handled by it
+ final var fragmentManager = getSupportFragmentManager();
+
if (bottomSheetHiddenOrCollapsed()) {
- final var fm = getSupportFragmentManager();
- final var fragment = fm.findFragmentById(R.id.fragment_holder);
+ final var fragment = fragmentManager.findFragmentById(R.id.fragment_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
// delegate the back press to it
if (fragment instanceof BackPressable backPressable && backPressable.onBackPressed()) {
return;
}
} else {
- final var fragmentPlayer = getSupportFragmentManager()
- .findFragmentById(R.id.fragment_player_holder);
+ final var player = fragmentManager.findFragmentById(R.id.fragment_player_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
// delegate the back press to it
- if (fragmentPlayer instanceof BackPressable backPressable
- && !backPressable.onBackPressed()) {
+ if (player instanceof BackPressable backPressable && !backPressable.onBackPressed()) {
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
.setState(BottomSheetBehavior.STATE_COLLAPSED);
return;
}
}
- if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
+ if (fragmentManager.getBackStackEntryCount() == 1) {
finish();
} else {
super.onBackPressed();
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 8fcf1e663..20ea3c710 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -73,7 +73,6 @@ import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.AudioStream;
@@ -1011,19 +1010,6 @@ public final class VideoDetailFragment
updateTabLayoutVisibility();
}
- public void scrollToComment(final CommentsInfoItem comment) {
- final int commentsTabPos = pageAdapter.getItemPositionByTitle(COMMENTS_TAB_TAG);
- final var fragment = pageAdapter.getItem(commentsTabPos);
-
- // TODO: Implement the scrolling with Compose.
- // unexpand the app bar only if scrolling to the comment succeeded
-// if (fragment instanceof CommentsFragment commentsFragment &&
-// commentsFragment.scrollToComment(comment)) {
-// binding.appBarLayout.setExpanded(false, false);
-// binding.viewPager.setCurrentItem(commentsTabPos, false);
-// }
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Play Utils
//////////////////////////////////////////////////////////////////////////*/
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index ed4f50dd0..d3be40151 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentsDisabledException
@@ -91,7 +92,17 @@ private class CommentDataProvider : PreviewParameterProvider
Date: Thu, 29 Aug 2024 07:24:03 +0530
Subject: [PATCH 57/93] Implement copy on long click
---
.../ui/components/common/DescriptionText.kt | 26 +++++++++--------
.../ui/components/video/comment/Comment.kt | 28 +++++++++++++++----
2 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
index 59ce07c94..40c5903c5 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/DescriptionText.kt
@@ -6,7 +6,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.TextStyle
@@ -23,21 +22,24 @@ fun DescriptionText(
maxLines: Int = Int.MAX_VALUE,
style: TextStyle = LocalTextStyle.current
) {
- // TODO: Handle links and hashtags, Markdown.
- val parsedDescription = remember(description) {
- if (description.type == Description.HTML) {
- val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
- AnnotatedString.fromHtml(description.content, styles)
- } else {
- AnnotatedString(description.content, ParagraphStyle())
- }
- }
-
Text(
modifier = modifier,
- text = parsedDescription,
+ text = rememberParsedDescription(description),
maxLines = maxLines,
style = style,
overflow = overflow
)
}
+
+@Composable
+fun rememberParsedDescription(description: Description): AnnotatedString {
+ // TODO: Handle links and hashtags, Markdown.
+ return remember(description) {
+ if (description.type == Description.HTML) {
+ val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
+ AnnotatedString.fromHtml(description.content, styles)
+ } else {
+ AnnotatedString(description.content)
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index f86c4807c..e5bdc6b91 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -1,9 +1,13 @@
package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
+import android.os.Build
+import android.widget.Toast
import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -27,6 +31,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@@ -46,23 +51,34 @@ import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentsSource
-import org.schabi.newpipe.ui.components.common.DescriptionText
+import org.schabi.newpipe.ui.components.common.rememberParsedDescription
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun Comment(comment: CommentsInfoItem) {
+ val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
var isExpanded by rememberSaveable { mutableStateOf(false) }
var showReplies by rememberSaveable { mutableStateOf(false) }
+ val parsedDescription = rememberParsedDescription(comment.commentText)
Row(
modifier = Modifier
.animateContentSize()
- .clickable { isExpanded = !isExpanded }
+ .combinedClickable(
+ onLongClick = {
+ clipboardManager.setText(parsedDescription)
+ if (Build.VERSION.SDK_INT < 33) {
+ // Android 13 has its own "copied to clipboard" dialog
+ Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show()
+ }
+ },
+ onClick = { isExpanded = !isExpanded }
+ )
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
@@ -99,13 +115,13 @@ fun Comment(comment: CommentsInfoItem) {
Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
}
- DescriptionText(
- description = comment.commentText,
+ Text(
+ text = parsedDescription,
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyMedium,
+ style = MaterialTheme.typography.bodyMedium
)
Row(
From 4cac111b668c3a2127c6ac73cf4c44c74b4414db Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Thu, 29 Aug 2024 07:46:37 +0530
Subject: [PATCH 58/93] Reduce preview count
---
.../newpipe/ui/components/video/comment/CommentSection.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index d3be40151..5e0fc0f0a 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -144,7 +144,7 @@ private fun CommentRepliesPreview() {
isPinned = true,
isHeartedByUploader = true
)
- val replies = (1..100).map {
+ val replies = (1..10).map {
CommentsInfoItem(
commentText = Description("Reply $it", Description.PLAIN_TEXT),
uploaderName = "Test"
From 3785404618a533b1106bcf2874c25de6378505c8 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 30 Aug 2024 08:37:42 +0530
Subject: [PATCH 59/93] Display number of comments
---
.../schabi/newpipe/paging/CommentsSource.kt | 20 +-
.../ui/components/video/comment/Comment.kt | 8 +-
.../components/video/comment/CommentInfo.kt | 21 ++
.../video/comment/CommentSection.kt | 198 +++++++++++-------
.../newpipe/viewmodels/CommentsViewModel.kt | 36 +++-
.../newpipe/viewmodels/util/Resource.kt | 7 +
app/src/main/res/values/strings.xml | 4 +
7 files changed, 203 insertions(+), 91 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentInfo.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/viewmodels/util/Resource.kt
diff --git a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
index aec24a344..d92589732 100644
--- a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
+++ b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
@@ -8,13 +8,19 @@ import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfo
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.ui.components.video.comment.CommentInfo
import org.schabi.newpipe.util.NO_SERVICE_ID
class CommentsSource(
serviceId: Int,
- private val url: String?,
- private val repliesPage: Page?
+ private val url: String,
+ private val repliesPage: Page?,
+ private val commentInfo: CommentInfo? = null,
) : PagingSource() {
+ constructor(commentInfo: CommentInfo) : this(
+ commentInfo.serviceId, commentInfo.url, commentInfo.nextPage, commentInfo
+ )
+
init {
require(serviceId != NO_SERVICE_ID) { "serviceId is NO_SERVICE_ID" }
}
@@ -29,17 +35,11 @@ class CommentsSource(
val info = CommentsInfo.getMoreItems(service, url, it)
LoadResult.Page(info.items, null, info.nextPage)
} ?: run {
- val info = CommentsInfo.getInfo(service, url)
- if (info.isCommentsDisabled) {
- LoadResult.Error(CommentsDisabledException())
- } else {
- LoadResult.Page(info.relatedItems, null, info.nextPage)
- }
+ val info = commentInfo ?: CommentInfo(CommentsInfo.getInfo(service, url))
+ LoadResult.Page(info.comments, null, info.nextPage)
}
}
}
override fun getRefreshKey(state: PagingState) = null
}
-
-class CommentsDisabledException : RuntimeException()
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index e5bdc6b91..d02dc1489 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -166,7 +166,13 @@ fun Comment(comment: CommentsInfoItem) {
.cachedIn(coroutineScope)
}
- CommentSection(parentComment = comment, commentsFlow = flow)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(
+ commentsFlow = flow,
+ commentCount = comment.replyCount,
+ parentComment = comment
+ )
+ }
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentInfo.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentInfo.kt
new file mode 100644
index 000000000..2c62739e2
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentInfo.kt
@@ -0,0 +1,21 @@
+package org.schabi.newpipe.ui.components.video.comment
+
+import androidx.compose.runtime.Immutable
+import org.schabi.newpipe.extractor.Page
+import org.schabi.newpipe.extractor.comments.CommentsInfo
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+
+@Immutable
+class CommentInfo(
+ val serviceId: Int,
+ val url: String,
+ val comments: List,
+ val nextPage: Page?,
+ val commentCount: Int,
+ val isCommentsDisabled: Boolean
+) {
+ constructor(commentsInfo: CommentsInfo) : this(
+ commentsInfo.serviceId, commentsInfo.url, commentsInfo.relatedItems, commentsInfo.nextPage,
+ commentsInfo.commentsCount, commentsInfo.isCommentsDisabled
+ )
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index 5e0fc0f0a..d2e0ea122 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -7,20 +7,19 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.res.pluralStringResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.LoadState
-import androidx.paging.LoadStates
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
@@ -30,106 +29,157 @@ import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.paging.CommentsDisabledException
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.viewmodels.CommentsViewModel
+import org.schabi.newpipe.viewmodels.util.Resource
@Composable
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
- CommentSection(commentsFlow = commentsViewModel.comments)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
+ CommentSection(state, commentsViewModel.comments)
+ }
+}
+
+@Composable
+private fun CommentSection(
+ uiState: Resource,
+ commentsFlow: Flow>
+) {
+ when (uiState) {
+ is Resource.Loading -> LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+
+ is Resource.Success -> {
+ val commentsInfo = uiState.data
+ CommentSection(
+ commentsFlow = commentsFlow,
+ commentCount = commentsInfo.commentCount,
+ isCommentsDisabled = commentsInfo.isCommentsDisabled
+ )
+ }
+
+ is Resource.Error -> {
+ // This is not rendered as VideoDetailFragment handles errors
+ }
+ }
}
@Composable
fun CommentSection(
+ commentsFlow: Flow>,
+ commentCount: Int,
parentComment: CommentsInfoItem? = null,
- commentsFlow: Flow>
+ isCommentsDisabled: Boolean = false,
) {
val comments = commentsFlow.collectAsLazyPagingItems()
- val itemCount by remember { derivedStateOf { comments.itemCount } }
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val state = rememberLazyListState()
- Surface(color = MaterialTheme.colorScheme.background) {
- LazyColumnScrollbar(state = state) {
- LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop), state = state) {
- if (parentComment != null) {
+ LazyColumnScrollbar(state = state) {
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
+ ) {
+ if (parentComment != null) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
+ }
+
+ if (comments.itemCount == 0) {
+ item {
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val message = if (refresh is LoadState.Error) {
+ R.string.error_unable_to_load_comments
+ } else if (isCommentsDisabled) {
+ R.string.comments_are_disabled
+ } else {
+ R.string.no_comments
+ }
+ NoItemsMessage(message)
+ }
+ }
+ } else {
+ // The number of replies is already shown in the main comment section
+ if (parentComment == null) {
item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ text = pluralStringResource(R.plurals.comments, commentCount, commentCount),
+ fontWeight = FontWeight.Bold
+ )
}
}
- if (itemCount == 0) {
- item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else {
- val error = (refresh as? LoadState.Error)?.error
- val message = if (error is CommentsDisabledException) {
- R.string.comments_are_disabled
- } else {
- R.string.no_comments
- }
- NoItemsMessage(message)
- }
- }
- } else {
- items(itemCount) {
- Comment(comment = comments[it]!!)
- }
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!)
}
}
}
}
}
-private class CommentDataProvider : PreviewParameterProvider> {
- private val notLoading = LoadState.NotLoading(true)
-
- override val values = sequenceOf(
- // Normal view
- PagingData.from(
- listOf(
- CommentsInfoItem(
- commentText = Description(
- "Comment 1\n\nThis line should be hidden by default.",
- Description.PLAIN_TEXT
- ),
- uploaderName = "Test",
- replies = Page(""),
- replyCount = 10
- )
- ) + (2..10).map {
- CommentsInfoItem(
- commentText = Description("Comment $it", Description.PLAIN_TEXT),
- uploaderName = "Test"
- )
- }
- ),
- // Comments disabled
- PagingData.from(
- listOf(),
- LoadStates(LoadState.Error(CommentsDisabledException()), notLoading, notLoading)
- ),
- // No comments
- PagingData.from(
- listOf(),
- LoadStates(notLoading, notLoading, notLoading)
- )
- )
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CommentSectionLoadingPreview() {
+ AppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(uiState = Resource.Loading, commentsFlow = flowOf())
+ }
+ }
}
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-private fun CommentSectionPreview(
- @PreviewParameter(CommentDataProvider::class) pagingData: PagingData
-) {
+private fun CommentSectionSuccessPreview() {
+ val comments = listOf(
+ CommentsInfoItem(
+ commentText = Description(
+ "Comment 1\n\nThis line should be hidden by default.",
+ Description.PLAIN_TEXT
+ ),
+ uploaderName = "Test",
+ replies = Page(""),
+ replyCount = 10
+ )
+ ) + (2..10).map {
+ CommentsInfoItem(
+ commentText = Description("Comment $it", Description.PLAIN_TEXT),
+ uploaderName = "Test"
+ )
+ }
+
AppTheme {
- CommentSection(commentsFlow = flowOf(pagingData))
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(
+ uiState = Resource.Success(
+ CommentInfo(
+ serviceId = 1, url = "", comments = comments, nextPage = null,
+ commentCount = 10, isCommentsDisabled = false
+ )
+ ),
+ commentsFlow = flowOf(PagingData.from(comments))
+ )
+ }
+ }
+}
+
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CommentSectionErrorPreview() {
+ AppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(uiState = Resource.Error(RuntimeException()), commentsFlow = flowOf())
+ }
}
}
@@ -153,6 +203,8 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- CommentSection(parentComment = comment, commentsFlow = flow)
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection(parentComment = comment, commentsFlow = flow, commentCount = 10)
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt b/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
index 62babb186..007292498 100644
--- a/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt
@@ -6,17 +6,39 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import org.schabi.newpipe.extractor.comments.CommentsInfo
import org.schabi.newpipe.paging.CommentsSource
-import org.schabi.newpipe.util.KEY_SERVICE_ID
+import org.schabi.newpipe.ui.components.video.comment.CommentInfo
import org.schabi.newpipe.util.KEY_URL
-import org.schabi.newpipe.util.NO_SERVICE_ID
+import org.schabi.newpipe.viewmodels.util.Resource
class CommentsViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
- private val serviceId = savedStateHandle[KEY_SERVICE_ID] ?: NO_SERVICE_ID
- private val url = savedStateHandle.get(KEY_URL)
+ val uiState = savedStateHandle.getStateFlow(KEY_URL, "")
+ .map {
+ try {
+ Resource.Success(CommentInfo(CommentsInfo.getInfo(it)))
+ } catch (e: Exception) {
+ Resource.Error(e)
+ }
+ }
+ .flowOn(Dispatchers.IO)
+ .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Resource.Loading)
- val comments = Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentsSource(serviceId, url, null)
- }.flow
+ @OptIn(ExperimentalCoroutinesApi::class)
+ val comments = uiState
+ .filterIsInstance>()
+ .flatMapLatest {
+ Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentsSource(it.data)
+ }.flow
+ }
.cachedIn(viewModelScope)
}
diff --git a/app/src/main/java/org/schabi/newpipe/viewmodels/util/Resource.kt b/app/src/main/java/org/schabi/newpipe/viewmodels/util/Resource.kt
new file mode 100644
index 000000000..38bc81391
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/viewmodels/util/Resource.kt
@@ -0,0 +1,7 @@
+package org.schabi.newpipe.viewmodels.util
+
+sealed class Resource {
+ data object Loading : Resource()
+ class Success(val data: T) : Resource()
+ class Error(val throwable: Throwable) : Resource()
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 938a2497d..0539a0daf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -856,4 +856,8 @@
Show less
The settings in the export being imported use a vulnerable format that was deprecated since NewPipe 0.27.0. Make sure the export being imported is from a trusted source, and prefer using only exports obtained from NewPipe 0.27.0 or newer in the future. Support for importing settings in this vulnerable format will soon be removed completely, and then old versions of NewPipe will not be able to import settings of exports from new versions anymore.
Next
+
+ - %d comment
+ - %d comments
+
From 62d4044d6cc4fea92aa5325353d96cfc181fbf8b Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 30 Aug 2024 08:58:57 +0530
Subject: [PATCH 60/93] Make lazy column scrollbars red
---
.../schabi/newpipe/ui/components/items/ItemList.kt | 11 ++++++++++-
.../ui/components/video/comment/CommentSection.kt | 11 ++++++++++-
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
index 506687d12..4f58e3e32 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
@@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
@@ -18,6 +19,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceManager
import androidx.window.core.layout.WindowWidthSizeClass
import my.nanihadesuka.compose.LazyColumnScrollbar
+import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.InfoItem
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem
@@ -25,6 +27,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.info_list.ItemViewMode
import org.schabi.newpipe.ui.components.items.playlist.PlaylistListItem
import org.schabi.newpipe.ui.components.items.stream.StreamListItem
+import org.schabi.newpipe.ui.theme.md_theme_dark_primary
import org.schabi.newpipe.util.DependentPreferenceHelper
import org.schabi.newpipe.util.NavigationHelper
@@ -72,7 +75,13 @@ fun ItemList(
} else {
val state = rememberLazyListState()
- LazyColumnScrollbar(state = state) {
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
+ ) {
LazyColumn(modifier = nestedScrollModifier, state = state) {
listHeader()
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index d2e0ea122..bdd72cea5 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -11,6 +11,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
@@ -25,6 +26,7 @@ import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
+import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
@@ -32,6 +34,7 @@ import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.ui.theme.md_theme_dark_primary
import org.schabi.newpipe.viewmodels.CommentsViewModel
import org.schabi.newpipe.viewmodels.util.Resource
@@ -77,7 +80,13 @@ fun CommentSection(
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val state = rememberLazyListState()
- LazyColumnScrollbar(state = state) {
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
+ ) {
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
state = state
From 823d4a041f59abf53242097e8bff58af7b849faa Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Fri, 30 Aug 2024 16:59:15 +0530
Subject: [PATCH 61/93] Improve loading indicator positioning
---
.../list/comments/CommentsFragment.kt | 6 +-
.../ui/components/video/comment/Comment.kt | 27 +--
.../video/comment/CommentRepliesDialog.kt | 132 ++++++++++++++
.../video/comment/CommentSection.kt | 165 +++++++-----------
4 files changed, 204 insertions(+), 126 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
index 2c6898d7a..6e20e1425 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt
@@ -3,6 +3,8 @@ package org.schabi.newpipe.fragments.list.comments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
@@ -18,7 +20,9 @@ class CommentsFragment : Fragment() {
savedInstanceState: Bundle?
) = content {
AppTheme {
- CommentSection()
+ Surface(color = MaterialTheme.colorScheme.background) {
+ CommentSection()
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index d02dc1489..59a451392 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -15,9 +15,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -25,7 +23,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -42,22 +39,18 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import androidx.paging.cachedIn
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.paging.CommentsSource
import org.schabi.newpipe.ui.components.common.rememberParsedDescription
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Comment(comment: CommentsInfoItem) {
val clipboardManager = LocalClipboardManager.current
@@ -157,23 +150,7 @@ fun Comment(comment: CommentsInfoItem) {
}
if (showReplies) {
- ModalBottomSheet(onDismissRequest = { showReplies = false }) {
- val coroutineScope = rememberCoroutineScope()
- val flow = remember {
- Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentsSource(comment.serviceId, comment.url, comment.replies)
- }.flow
- .cachedIn(coroutineScope)
- }
-
- Surface(color = MaterialTheme.colorScheme.background) {
- CommentSection(
- commentsFlow = flow,
- commentCount = comment.replyCount,
- parentComment = comment
- )
- }
- }
+ CommentRepliesDialog(comment, onDismissRequest = { showReplies = false })
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
new file mode 100644
index 000000000..30131d91e
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -0,0 +1,132 @@
+package org.schabi.newpipe.ui.components.video.comment
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.paging.LoadState
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import androidx.paging.compose.collectAsLazyPagingItems
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import my.nanihadesuka.compose.LazyColumnScrollbar
+import my.nanihadesuka.compose.ScrollbarSettings
+import org.schabi.newpipe.R
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.paging.CommentsSource
+import org.schabi.newpipe.ui.components.common.LoadingIndicator
+import org.schabi.newpipe.ui.components.common.NoItemsMessage
+import org.schabi.newpipe.ui.theme.AppTheme
+import org.schabi.newpipe.ui.theme.md_theme_dark_primary
+
+@Composable
+fun CommentRepliesDialog(
+ parentComment: CommentsInfoItem,
+ onDismissRequest: () -> Unit,
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val commentsFlow = remember {
+ Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
+ CommentsSource(parentComment.serviceId, parentComment.url, parentComment.replies)
+ }.flow
+ .cachedIn(coroutineScope)
+ }
+
+ CommentRepliesDialog(parentComment, commentsFlow, onDismissRequest)
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun CommentRepliesDialog(
+ parentComment: CommentsInfoItem,
+ commentsFlow: Flow>,
+ onDismissRequest: () -> Unit,
+) {
+ val comments = commentsFlow.collectAsLazyPagingItems()
+ val nestedScrollInterop = rememberNestedScrollInteropConnection()
+ val state = rememberLazyListState()
+
+ ModalBottomSheet(onDismissRequest = onDismissRequest) {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
+ ) {
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
+ ) {
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(thickness = 1.dp)
+ }
+
+ if (comments.itemCount == 0) {
+ item {
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val message = if (refresh is LoadState.Error) {
+ R.string.error_unable_to_load_comments
+ } else {
+ R.string.no_comments
+ }
+ NoItemsMessage(message)
+ }
+ }
+ } else {
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CommentRepliesDialogPreview() {
+ val comment = CommentsInfoItem(
+ commentText = Description("Hello world!", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ likeCount = 100,
+ isPinned = true,
+ isHeartedByUploader = true
+ )
+ val replies = (1..10).map {
+ CommentsInfoItem(
+ commentText = Description("Reply $it", Description.PLAIN_TEXT),
+ uploaderName = "Test"
+ )
+ }
+ val flow = flowOf(PagingData.from(replies))
+
+ AppTheme {
+ CommentRepliesDialog(comment, flow, onDismissRequest = {})
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index bdd72cea5..f643af69d 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -4,7 +4,6 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@@ -40,94 +39,86 @@ import org.schabi.newpipe.viewmodels.util.Resource
@Composable
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
- Surface(color = MaterialTheme.colorScheme.background) {
- val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
- CommentSection(state, commentsViewModel.comments)
- }
+ val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
+ CommentSection(state, commentsViewModel.comments)
}
@Composable
private fun CommentSection(
uiState: Resource,
commentsFlow: Flow>
-) {
- when (uiState) {
- is Resource.Loading -> LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
-
- is Resource.Success -> {
- val commentsInfo = uiState.data
- CommentSection(
- commentsFlow = commentsFlow,
- commentCount = commentsInfo.commentCount,
- isCommentsDisabled = commentsInfo.isCommentsDisabled
- )
- }
-
- is Resource.Error -> {
- // This is not rendered as VideoDetailFragment handles errors
- }
- }
-}
-
-@Composable
-fun CommentSection(
- commentsFlow: Flow>,
- commentCount: Int,
- parentComment: CommentsInfoItem? = null,
- isCommentsDisabled: Boolean = false,
) {
val comments = commentsFlow.collectAsLazyPagingItems()
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val state = rememberLazyListState()
- LazyColumnScrollbar(
- state = state,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
- ) {
- LazyColumn(
- modifier = Modifier.nestedScroll(nestedScrollInterop),
- state = state
+ Surface(color = MaterialTheme.colorScheme.background) {
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
) {
- if (parentComment != null) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
- }
- }
-
- if (comments.itemCount == 0) {
- item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else {
- val message = if (refresh is LoadState.Error) {
- R.string.error_unable_to_load_comments
- } else if (isCommentsDisabled) {
- R.string.comments_are_disabled
- } else {
- R.string.no_comments
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
+ ) {
+ when (uiState) {
+ is Resource.Loading -> {
+ item {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
}
- NoItemsMessage(message)
}
- }
- } else {
- // The number of replies is already shown in the main comment section
- if (parentComment == null) {
- item {
- Text(
- modifier = Modifier.padding(start = 8.dp),
- text = pluralStringResource(R.plurals.comments, commentCount, commentCount),
- fontWeight = FontWeight.Bold
- )
- }
- }
- items(comments.itemCount) {
- Comment(comment = comments[it]!!)
+ is Resource.Success -> {
+ val commentInfo = uiState.data
+ val count = commentInfo.commentCount
+
+ if (commentInfo.isCommentsDisabled) {
+ item {
+ NoItemsMessage(R.string.comments_are_disabled)
+ }
+ } else if (count == 0) {
+ item {
+ NoItemsMessage(R.string.no_comments)
+ }
+ } else {
+ item {
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ text = pluralStringResource(R.plurals.comments, count, count),
+ fontWeight = FontWeight.Bold
+ )
+ }
+
+ when (comments.loadState.refresh) {
+ is LoadState.Loading -> {
+ item {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ }
+ }
+
+ is LoadState.Error -> {
+ item {
+ NoItemsMessage(R.string.error_unable_to_load_comments)
+ }
+ }
+
+ else -> {
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!)
+ }
+ }
+ }
+ }
+ }
+
+ is Resource.Error -> {
+ item {
+ NoItemsMessage(R.string.error_unable_to_load_comments)
+ }
+ }
}
}
}
@@ -191,29 +182,3 @@ private fun CommentSectionErrorPreview() {
}
}
}
-
-@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
-@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-private fun CommentRepliesPreview() {
- val comment = CommentsInfoItem(
- commentText = Description("Hello world!", Description.PLAIN_TEXT),
- uploaderName = "Test",
- likeCount = 100,
- isPinned = true,
- isHeartedByUploader = true
- )
- val replies = (1..10).map {
- CommentsInfoItem(
- commentText = Description("Reply $it", Description.PLAIN_TEXT),
- uploaderName = "Test"
- )
- }
- val flow = flowOf(PagingData.from(replies))
-
- AppTheme {
- Surface(color = MaterialTheme.colorScheme.background) {
- CommentSection(parentComment = comment, commentsFlow = flow, commentCount = 10)
- }
- }
-}
From 035c394cf6704c0af9077c01ebfa55b810d02140 Mon Sep 17 00:00:00 2001
From: Mihael_River <154642338+Stackoverflow-addict@users.noreply.github.com>
Date: Fri, 30 Aug 2024 16:32:42 +0200
Subject: [PATCH 62/93] Fixing the 404 page not found, when clicking on
"contribution notes" in multiple README.md's translated into different
languages (#11487)
Link to contribution notes wasn't working
* Update README.de.md, fix grammar in README.de.md
* Update README.asm.md
* Update README.fr.md
* Update README.hi.md
* Update README.it.md
* Update README.pa.md
* Update README.pt_BR.md
* Update README.ru.md
* Update README.sr.md
---------
Co-authored-by: Tobi
---
doc/README.asm.md | 2 +-
doc/README.de.md | 2 +-
doc/README.fr.md | 2 +-
doc/README.hi.md | 2 +-
doc/README.it.md | 2 +-
doc/README.pa.md | 2 +-
doc/README.pt_BR.md | 2 +-
doc/README.ru.md | 2 +-
doc/README.sr.md | 2 +-
9 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/doc/README.asm.md b/doc/README.asm.md
index 2a7c10f51..370319c15 100644
--- a/doc/README.asm.md
+++ b/doc/README.asm.md
@@ -106,7 +106,7 @@ NewPipe এ আপুনি ব্যৱহাৰ কৰা সেৱাৰ অ
## অৱদান
-আপোনাৰ ধাৰণা, অনুবাদ, ডিজাইন পৰিবৰ্তন, ক'ড পৰিষ্কাৰ কৰা, বা আনকি ডাঙৰ ক'ড পৰিৱৰ্তন হওক, সহায় সদায় আদৰণীয়। প্ৰতিটো অৱদানৰ লগে লগে এপটো ভাল হৈ পৰে, যিমানেই ডাঙৰ বা সৰু নহওক কিয়! যদি আপুনি জড়িত হ'ব বিচাৰে তেন্তে চাওক আমাৰ [অবদানৰ টোকা সমূহ](.github/CONTRIBUTING.md).
+আপোনাৰ ধাৰণা, অনুবাদ, ডিজাইন পৰিবৰ্তন, ক'ড পৰিষ্কাৰ কৰা, বা আনকি ডাঙৰ ক'ড পৰিৱৰ্তন হওক, সহায় সদায় আদৰণীয়। প্ৰতিটো অৱদানৰ লগে লগে এপটো ভাল হৈ পৰে, যিমানেই ডাঙৰ বা সৰু নহওক কিয়! যদি আপুনি জড়িত হ'ব বিচাৰে তেন্তে চাওক আমাৰ [অবদানৰ টোকা সমূহ](/.github/CONTRIBUTING.md).
## অনুদান
diff --git a/doc/README.de.md b/doc/README.de.md
index e269da05c..be7744332 100644
--- a/doc/README.de.md
+++ b/doc/README.de.md
@@ -126,7 +126,7 @@ So eine Aktion wird nicht unterstützt und du solltest sie nur in Erwägung zieh
## Beitrag
Egal ob du neue Ideen, Übersetzungen, Designvorschläge, kleine Code-Bereinigungen, oder sogar große Code-Verbesserungen hast, jegliche Unterstützung ist immer gern gesehen.
Die App wird mit _jedem_ Beitrag besser und besser - egal wie viel Arbeit in ihn gesteckt wird!
-Wenn du dich einbringen willst, sehe dir die [Beitragshinweise](.github/CONTRIBUTING.md) an.
+Wenn du dich einbringen willst, sieh dir die [Beitragshinweise](/.github/CONTRIBUTING.md) an.
diff --git a/doc/README.fr.md b/doc/README.fr.md
index 7d4673b69..864cc927a 100644
--- a/doc/README.fr.md
+++ b/doc/README.fr.md
@@ -109,7 +109,7 @@ Entre temps, si vous voulez changer de source pour une raison quelconque (par ex
## Contribuer
-Que vous ayez des idées, des traductions, des changements de design, du nettoyage de code, ou encore un changement de code majeur, toute aide est la bienvenue. L'app s'améliore un peu plus à chaque contribution, peu importe qu'elle soit grosse ou petite ! Si vous aimeriez être impliqué, jetez un coup d'oeil à nos [notes pour contribuer](.github/CONTRIBUTING.md).
+Que vous ayez des idées, des traductions, des changements de design, du nettoyage de code, ou encore un changement de code majeur, toute aide est la bienvenue. L'app s'améliore un peu plus à chaque contribution, peu importe qu'elle soit grosse ou petite ! Si vous aimeriez être impliqué, jetez un coup d'oeil à nos [notes pour contribuer](/.github/CONTRIBUTING.md).
diff --git a/doc/README.hi.md b/doc/README.hi.md
index 282e75420..d503f43a5 100644
--- a/doc/README.hi.md
+++ b/doc/README.hi.md
@@ -105,7 +105,7 @@ NewPipe पर कई सेवाएँ उपलब्ध हैं। हम
चाहे आप अपने विचार जोड़ना चाहे, या अनुवाद, डिज़ाइन में बदलाव, कोड में सफ़ाई, या कोड में भारी बदलाव, सहायता ज़रूर करें।
जितने योगदान हो, ऐप उतनी ही बेहतर होती जाती है!
-अगर आप योगदान करना चाहते हैं, हमारे [योगदान के दिशानिर्देश](.github/CONTRIBUTING.md) देखें।
+अगर आप योगदान करना चाहते हैं, हमारे [योगदान के दिशानिर्देश](/.github/CONTRIBUTING.md) देखें।
diff --git a/doc/README.it.md b/doc/README.it.md
index 55ae12380..8bf1eb380 100644
--- a/doc/README.it.md
+++ b/doc/README.it.md
@@ -107,7 +107,7 @@ Nel frattempo, se vuoi cambiare fonte per la stessa ragione (ad es. la funzional
## Contribuire
-Se hai idee, traduzioni, cambiamenti di *design*, pulizia di codice, o addirittura grossi cambiamenti di codice, l'aiuto è sempre apprezzato. L'app diventa sempre meglio con ogni contribuzione, non importa quanto grande o piccola essa sia! Se ti piacerebbe essere parte del progetto, vedi le nostre [note di contribuzione](.github/CONTRIBUTING.md).
+Se hai idee, traduzioni, cambiamenti di *design*, pulizia di codice, o addirittura grossi cambiamenti di codice, l'aiuto è sempre apprezzato. L'app diventa sempre meglio con ogni contribuzione, non importa quanto grande o piccola essa sia! Se ti piacerebbe essere parte del progetto, vedi le nostre [note di contribuzione](/.github/CONTRIBUTING.md).
diff --git a/doc/README.pa.md b/doc/README.pa.md
index 321e6b7d0..0ad5e0625 100644
--- a/doc/README.pa.md
+++ b/doc/README.pa.md
@@ -105,7 +105,7 @@ NewPipe ਤੁਹਾਡੇ ਦੁਆਰਾ ਵਰਤੀ ਜਾ ਰਹੀ ਸੇ
ਨੋਟ: ਜਦੋਂ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਵਿੱਚ ਇੱਕ ਡੇਟਾਬੇਸ ਨੂੰ ਆਯਾਤ ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਹਮੇਸ਼ਾਂ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਇਹ ਉਹੀ ਹੈ ਜੋ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਤੋਂ ਨਿਰਯਾਤ ਕੀਤਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਤੋਂ ਇਲਾਵਾ ਕਿਸੇ ਏਪੀਕੇ ਤੋਂ ਨਿਰਯਾਤ ਕੀਤੇ ਡੇਟਾਬੇਸ ਨੂੰ ਆਯਾਤ ਕਰਦੇ ਹੋ, ਤਾਂ ਇਹ ਚੀਜ਼ਾਂ ਨੂੰ ਤੋੜ ਸਕਦਾ ਹੈ। ਅਜਿਹੀ ਕਾਰਵਾਈ ਅਸਮਰਥਿਤ ਹੈ, ਅਤੇ ਤੁਹਾਨੂੰ ਅਜਿਹਾ ਉਦੋਂ ਹੀ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜਦੋਂ ਤੁਹਾਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਯਕੀਨ ਹੋਵੇ ਕਿ ਤੁਸੀਂ ਜਾਣਦੇ ਹੋ ਕਿ ਤੁਸੀਂ ਕੀ ਕਰ ਰਹੇ ਹੋ।
## ਯੋਗਦਾਨ
-ਭਾਵੇਂ ਤੁਹਾਡੇ ਕੋਲ ਵਿਚਾਰ, ਅਨੁਵਾਦ, ਡਿਜ਼ਾਈਨ ਤਬਦੀਲੀਆਂ, ਕੋਡ ਦੀ ਸਫਾਈ, ਜਾਂ ਇੱਥੋਂ ਤੱਕ ਕਿ ਵੱਡੀਆਂ ਕੋਡ ਤਬਦੀਲੀਆਂ ਹੋਣ, ਮਦਦ ਦਾ ਹਮੇਸ਼ਾ ਸਵਾਗਤ ਹੈ। ਐਪ ਹਰੇਕ ਯੋਗਦਾਨ ਦੇ ਨਾਲ ਬਿਹਤਰ ਅਤੇ ਬਿਹਤਰ ਹੋ ਜਾਂਦੀ ਹੈ, ਚਾਹੇ ਉਹ ਕਿੰਨਾ ਵੱਡਾ ਜਾਂ ਛੋਟਾ ਹੋਵੇ! ਜੇਕਰ ਤੁਸੀਂ ਸ਼ਾਮਲ ਹੋਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਸਾਡੀ ਜਾਂਚ ਕਰੋ [contribution notes](.github/CONTRIBUTING.md).
+ਭਾਵੇਂ ਤੁਹਾਡੇ ਕੋਲ ਵਿਚਾਰ, ਅਨੁਵਾਦ, ਡਿਜ਼ਾਈਨ ਤਬਦੀਲੀਆਂ, ਕੋਡ ਦੀ ਸਫਾਈ, ਜਾਂ ਇੱਥੋਂ ਤੱਕ ਕਿ ਵੱਡੀਆਂ ਕੋਡ ਤਬਦੀਲੀਆਂ ਹੋਣ, ਮਦਦ ਦਾ ਹਮੇਸ਼ਾ ਸਵਾਗਤ ਹੈ। ਐਪ ਹਰੇਕ ਯੋਗਦਾਨ ਦੇ ਨਾਲ ਬਿਹਤਰ ਅਤੇ ਬਿਹਤਰ ਹੋ ਜਾਂਦੀ ਹੈ, ਚਾਹੇ ਉਹ ਕਿੰਨਾ ਵੱਡਾ ਜਾਂ ਛੋਟਾ ਹੋਵੇ! ਜੇਕਰ ਤੁਸੀਂ ਸ਼ਾਮਲ ਹੋਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਸਾਡੀ ਜਾਂਚ ਕਰੋ [contribution notes](/.github/CONTRIBUTING.md).
diff --git a/doc/README.pt_BR.md b/doc/README.pt_BR.md
index 59ff65db3..73489bb41 100644
--- a/doc/README.pt_BR.md
+++ b/doc/README.pt_BR.md
@@ -106,7 +106,7 @@ Enquanto isso, se você quiser trocar de fontes por algum motivo (por exemplo, a
4. Importe os dados da etapa 1 via Configurações > Backup e Restauração > Importar Base de Dados
## Contribuições
-Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda. O aplicativo fica cada vez melhor a cada contribuição, não importa quão grande ou pequena! Se você quiser se envolver, verifique nossas [notas de contribuição](.github/CONTRIBUTING.md).
+Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda. O aplicativo fica cada vez melhor a cada contribuição, não importa quão grande ou pequena! Se você quiser se envolver, verifique nossas [notas de contribuição](/.github/CONTRIBUTING.md).
diff --git a/doc/README.ru.md b/doc/README.ru.md
index 35058c981..35867b8bf 100644
--- a/doc/README.ru.md
+++ b/doc/README.ru.md
@@ -106,7 +106,7 @@ NewPipe работает, извлекая необходимые данные
Примечание: когда вы импортируете базу данных в официальное приложение, убедитесь, что это именно та база данных, которую вы экспортировали _из_ официального приложения. Если вы импортируете базу данных, экспортированную из APK, отличного от официального приложения, это может привести к ошибке. Такое действие не поддерживается, и вы должны делать его только тогда, когда абсолютно уверены, что знаете, что делаете.
## Участие
-Если у вас есть идеи, переводы, изменения дизайна, очистка кода или даже серьезные изменения кода, помощь всегда приветствуется. Приложение становится всё лучше и лучше с каждым вкладом, независимо от того, большой он или маленький! Если вы хотите принять участие, ознакомьтесь с нашими [заметками об участии](.github/CONTRIBUTING.md).
+Если у вас есть идеи, переводы, изменения дизайна, очистка кода или даже серьезные изменения кода, помощь всегда приветствуется. Приложение становится всё лучше и лучше с каждым вкладом, независимо от того, большой он или маленький! Если вы хотите принять участие, ознакомьтесь с нашими [заметками об участии](/.github/CONTRIBUTING.md).
diff --git a/doc/README.sr.md b/doc/README.sr.md
index 7f0ee65b7..60a21ce69 100644
--- a/doc/README.sr.md
+++ b/doc/README.sr.md
@@ -104,7 +104,7 @@ NewPipe ради тако што преузима потребне податк
Напомена: када увозите базу података у званичну апликацију, увек се уверите да је то она коју сте извезли _из_ званичне апликације. Ако увезете базу података извезену из APK-а, који није званична апликација, то може покварити ствари. Таква радња није подржана и требало би да то урадите само када сте потпуно сигурни да знате шта радите.
## Допринос
-Без обзира да ли имате идеје, преводе, промене дизајна, чишћење кода или чак велике промене кода, помоћ је увек добродошла. Апликација постаје све боља и боља са сваким доприносом, без обзира колико је он велики или мали! Ако желите да се укључите, погледајте наше [напомене о доприносима](.github/CONTRIBUTING.md).
+Без обзира да ли имате идеје, преводе, промене дизајна, чишћење кода или чак велике промене кода, помоћ је увек добродошла. Апликација постаје све боља и боља са сваким доприносом, без обзира колико је он велики или мали! Ако желите да се укључите, погледајте наше [напомене о доприносима](/.github/CONTRIBUTING.md).
From 5017f4f05a7be1d480675a9fe4396d1df42abd8b Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Thu, 5 Sep 2024 09:23:00 +0530
Subject: [PATCH 63/93] Update dependencies
---
app/build.gradle | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0a2b24565..00e3ad659 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -286,12 +286,11 @@ dependencies {
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
// Jetpack Compose
- implementation(platform('androidx.compose:compose-bom:2024.08.00'))
- implementation 'androidx.compose.material3:material3:1.3.0-rc01'
- implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-rc01'
+ implementation(platform('androidx.compose:compose-bom:2024.09.00'))
+ implementation 'androidx.compose.material3:material3'
+ implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
- implementation 'androidx.compose.ui:ui-text:1.7.0-rc01' // Needed for parsing HTML to AnnotatedString
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
implementation 'androidx.paging:paging-compose:3.3.2'
implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'
From 3177ca6e8ac080db217a0d670068035474e9603e Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 11 Sep 2024 21:57:51 +0530
Subject: [PATCH 64/93] Avoid issues if context is a ContextWrapper
---
app/src/main/java/org/schabi/newpipe/ktx/Context.kt | 13 +++++++++++++
.../schabi/newpipe/ui/components/items/ItemList.kt | 4 ++--
.../ui/components/items/stream/StreamMenu.kt | 9 +++++----
.../newpipe/ui/components/video/comment/Comment.kt | 5 +----
.../video/comment/CommentRepliesHeader.kt | 4 +---
.../org/schabi/newpipe/util/NavigationHelper.java | 8 +++++---
6 files changed, 27 insertions(+), 16 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/ktx/Context.kt
diff --git a/app/src/main/java/org/schabi/newpipe/ktx/Context.kt b/app/src/main/java/org/schabi/newpipe/ktx/Context.kt
new file mode 100644
index 000000000..f2f4e9613
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ktx/Context.kt
@@ -0,0 +1,13 @@
+package org.schabi.newpipe.ktx
+
+import android.content.Context
+import android.content.ContextWrapper
+import androidx.fragment.app.FragmentActivity
+
+tailrec fun Context.findFragmentActivity(): FragmentActivity {
+ return when (this) {
+ is FragmentActivity -> this
+ is ContextWrapper -> baseContext.findFragmentActivity()
+ else -> throw IllegalStateException("Unable to find FragmentActivity")
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
index 4f58e3e32..237a57667 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
@@ -15,7 +15,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.stringResource
-import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceManager
import androidx.window.core.layout.WindowWidthSizeClass
import my.nanihadesuka.compose.LazyColumnScrollbar
@@ -25,6 +24,7 @@ import org.schabi.newpipe.extractor.InfoItem
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.info_list.ItemViewMode
+import org.schabi.newpipe.ktx.findFragmentActivity
import org.schabi.newpipe.ui.components.items.playlist.PlaylistListItem
import org.schabi.newpipe.ui.components.items.stream.StreamListItem
import org.schabi.newpipe.ui.theme.md_theme_dark_primary
@@ -40,7 +40,7 @@ fun ItemList(
val context = LocalContext.current
val onClick = remember {
{ item: InfoItem ->
- val fragmentManager = (context as FragmentActivity).supportFragmentManager
+ val fragmentManager = context.findFragmentActivity().supportFragmentManager
if (item is StreamInfoItem) {
NavigationHelper.openVideoDetailFragment(
context, fragmentManager, item.serviceId, item.url, item.name, null, false
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamMenu.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamMenu.kt
index 2bb143db8..2902aa660 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamMenu.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamMenu.kt
@@ -8,12 +8,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.viewmodel.compose.viewModel
import org.schabi.newpipe.R
import org.schabi.newpipe.database.stream.model.StreamEntity
import org.schabi.newpipe.download.DownloadDialog
import org.schabi.newpipe.extractor.stream.StreamInfoItem
+import org.schabi.newpipe.ktx.findFragmentActivity
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog
import org.schabi.newpipe.local.dialog.PlaylistDialog
import org.schabi.newpipe.player.helper.PlayerHolder
@@ -84,7 +84,7 @@ fun StreamMenu(
) { info ->
// TODO: Use an AlertDialog composable instead.
val downloadDialog = DownloadDialog(context, info)
- val fragmentManager = (context as FragmentActivity).supportFragmentManager
+ val fragmentManager = context.findFragmentActivity().supportFragmentManager
downloadDialog.show(fragmentManager, "downloadDialog")
}
}
@@ -97,7 +97,7 @@ fun StreamMenu(
PlaylistDialog.createCorrespondingDialog(context, list) { dialog ->
val tag = if (dialog is PlaylistAppendDialog) "append" else "create"
dialog.show(
- (context as FragmentActivity).supportFragmentManager,
+ context.findFragmentActivity().supportFragmentManager,
"StreamDialogEntry@${tag}_playlist"
)
}
@@ -131,7 +131,8 @@ fun StreamMenu(
SparseItemUtil.fetchUploaderUrlIfSparse(
context, stream.serviceId, stream.url, stream.uploaderUrl
) { url ->
- NavigationHelper.openChannelFragment(context as FragmentActivity, stream, url)
+ val activity = context.findFragmentActivity()
+ NavigationHelper.openChannelFragment(activity, stream, url)
}
}
)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index 59a451392..baf0c7106 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -38,7 +38,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
-import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
@@ -84,9 +83,7 @@ fun Comment(comment: CommentsInfoItem) {
.size(42.dp)
.clip(CircleShape)
.clickable {
- NavigationHelper.openCommentAuthorIfPresent(
- context as FragmentActivity, comment
- )
+ NavigationHelper.openCommentAuthorIfPresent(context, comment)
}
)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 64d391b08..3a10fe021 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -22,7 +22,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
@@ -45,8 +44,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
) {
Row(
modifier = Modifier.clickable {
- val activity = context as FragmentActivity
- NavigationHelper.openCommentAuthorIfPresent(activity, comment)
+ NavigationHelper.openCommentAuthorIfPresent(context, comment)
},
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
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 2f8952afa..139381867 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -48,6 +48,7 @@ import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
+import org.schabi.newpipe.ktx.ContextKt;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
@@ -483,19 +484,20 @@ public final class NavigationHelper {
* Opens the comment author channel fragment, if the {@link CommentsInfoItem#getUploaderUrl()}
* of {@code comment} is non-null. Shows a UI-error snackbar if something goes wrong.
*
- * @param activity the activity with the fragment manager and in which to show the snackbar
+ * @param context the context to use for opening the fragment
* @param comment the comment whose uploader/author will be opened
*/
- public static void openCommentAuthorIfPresent(@NonNull final FragmentActivity activity,
+ public static void openCommentAuthorIfPresent(@NonNull final Context context,
@NonNull final CommentsInfoItem comment) {
if (isEmpty(comment.getUploaderUrl())) {
return;
}
try {
+ final var activity = ContextKt.findFragmentActivity(context);
openChannelFragment(activity.getSupportFragmentManager(), comment.getServiceId(),
comment.getUploaderUrl(), comment.getUploaderName());
} catch (final Exception e) {
- ErrorUtil.showUiErrorSnackbar(activity, "Opening channel fragment", e);
+ ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e);
}
}
From b399030e19c8ce41e4bddc0e06460f6087de2f16 Mon Sep 17 00:00:00 2001
From: Siddhesh Naik <87667048+snaik20@users.noreply.github.com>
Date: Tue, 22 Oct 2024 00:47:26 +0530
Subject: [PATCH 65/93] Settings redesign debug page (#10876)
Initial Work for Settings Page with Jetpack Compose
- Implemented a new settings page using Jetpack Compose.
- Added a new settings option to enable the redesigned settings page.
- This option allows for gradual integration and testing of the new
settings page, minimizing disruptions to current functionality.
Plan for Settings Items:
- Jetpack Compose does not have a direct equivalent to the
Preference/settings library.
- We could consider using third-party libraries that offer preference
items as composables.
- However, these libraries may be incomplete or lack active development.
- Given our specific needs for only a subset of preference types,
creating custom composables would be beneficial.
- This approach allows for fine-tuning the components to our specific
use case.
---
app/build.gradle | 32 +++++--
app/src/main/AndroidManifest.xml | 5 ++
app/src/main/java/org/schabi/newpipe/App.java | 2 +
.../main/java/org/schabi/newpipe/AppModule.kt | 22 +++++
.../schabi/newpipe/settings/DebugScreen.kt | 27 ++++++
.../schabi/newpipe/settings/SettingsScreen.kt | 23 +++++
.../newpipe/settings/SettingsV2Activity.kt | 85 +++++++++++++++++++
.../settings/viewmodel/SettingsViewModel.kt | 39 +++++++++
.../org/schabi/newpipe/ui/SwitchPreference.kt | 53 ++++++++++++
.../org/schabi/newpipe/ui/TextPreference.kt | 66 ++++++++++++++
.../schabi/newpipe/util/NavigationHelper.java | 10 ++-
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 | 7 ++
build.gradle | 1 +
15 files changed, 364 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/AppModule.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt
create mode 100644 app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt
diff --git a/app/build.gradle b/app/build.gradle
index 92fd2a7e9..8f7036e03 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,6 +10,7 @@ plugins {
id "checkstyle"
id "org.sonarqube" version "4.0.0.2929"
id "org.jetbrains.kotlin.plugin.compose" version "${kotlin_version}"
+ id 'com.google.dagger.hilt.android'
}
android {
@@ -190,6 +191,10 @@ sonar {
}
}
+kapt {
+ correctErrorTypes true
+}
+
dependencies {
/** Desugaring **/
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.4'
@@ -200,7 +205,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.2'
+ implementation 'com.github.teamnewpipe:newpipeextractor:v0.24.2'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
@@ -285,20 +290,29 @@ dependencies {
// Date and time formatting
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
- // Jetpack Compose
- implementation(platform('androidx.compose:compose-bom:2024.06.00'))
- implementation 'androidx.compose.material3:material3:1.3.0-beta05'
- implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-beta04'
- implementation 'androidx.activity:activity-compose'
+ // Jetpack Compose BOM group
+ implementation(platform('androidx.compose:compose-bom:2024.09.03'))
+ implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.ui:ui-tooling-preview'
- implementation 'androidx.compose.ui:ui-text:1.7.0-beta07' // Needed for parsing HTML to AnnotatedString
- implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
+ implementation 'androidx.compose.ui:ui-text' // Needed for parsing HTML to AnnotatedString
+
+ // Jetpack Compose related dependencies
+ implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0'
+ implementation 'androidx.activity:activity-compose:1.9.2'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6'
implementation 'androidx.paging:paging-compose:3.3.2'
- implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'
+ implementation "androidx.navigation:navigation-compose:2.8.2"
// Coroutines interop
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.8.1'
+ // Hilt
+ implementation("com.google.dagger:hilt-android:2.51.1")
+ kapt("com.google.dagger:hilt-compiler:2.51.1")
+
+ // Scroll
+ implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'
+
/** Debugging **/
// Memory leak detection
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d11de9f47..c44f8bf2c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -77,6 +77,11 @@
android:exported="false"
android:label="@string/settings" />
+
+
.
*/
+@HiltAndroidApp
public class App extends Application implements ImageLoaderFactory {
public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
private static final String TAG = App.class.toString();
diff --git a/app/src/main/java/org/schabi/newpipe/AppModule.kt b/app/src/main/java/org/schabi/newpipe/AppModule.kt
new file mode 100644
index 000000000..0aaf2f72b
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/AppModule.kt
@@ -0,0 +1,22 @@
+package org.schabi.newpipe
+
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.preference.PreferenceManager
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+class AppModule {
+
+ @Provides
+ @Singleton
+ fun providesSharedPreference(@ApplicationContext context: Context): SharedPreferences {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt b/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt
new file mode 100644
index 000000000..ac08dd36b
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt
@@ -0,0 +1,27 @@
+package org.schabi.newpipe.settings
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import org.schabi.newpipe.R
+import org.schabi.newpipe.settings.viewmodel.SettingsViewModel
+import org.schabi.newpipe.ui.SwitchPreference
+import org.schabi.newpipe.ui.theme.SizeTokens
+
+@Composable
+fun DebugScreen(viewModel: SettingsViewModel, modifier: Modifier = Modifier) {
+
+ val settingsLayoutRedesign by viewModel.settingsLayoutRedesign.collectAsState()
+
+ Column(modifier = modifier) {
+ SwitchPreference(
+ modifier = Modifier.padding(SizeTokens.SpacingExtraSmall),
+ R.string.settings_layout_redesign,
+ settingsLayoutRedesign,
+ viewModel::toggleSettingsLayoutRedesign
+ )
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt b/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt
new file mode 100644
index 000000000..5bd8f2b08
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt
@@ -0,0 +1,23 @@
+package org.schabi.newpipe.settings
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import org.schabi.newpipe.R
+import org.schabi.newpipe.ui.TextPreference
+
+@Composable
+fun SettingsScreen(
+ onSelectSettingOption: (SettingsScreenKey) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(modifier = modifier) {
+ TextPreference(
+ title = R.string.settings_category_debug_title,
+ onClick = { onSelectSettingOption(SettingsScreenKey.DEBUG) }
+ )
+ HorizontalDivider(color = Color.Black)
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt b/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt
new file mode 100644
index 000000000..821ff0187
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt
@@ -0,0 +1,85 @@
+package org.schabi.newpipe.settings
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import dagger.hilt.android.AndroidEntryPoint
+import org.schabi.newpipe.R
+import org.schabi.newpipe.settings.viewmodel.SettingsViewModel
+import org.schabi.newpipe.ui.Toolbar
+import org.schabi.newpipe.ui.theme.AppTheme
+
+const val SCREEN_TITLE_KEY = "SCREEN_TITLE_KEY"
+
+@AndroidEntryPoint
+class SettingsV2Activity : ComponentActivity() {
+
+ private val settingsViewModel: SettingsViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ val navController = rememberNavController()
+ var screenTitle by remember { mutableIntStateOf(SettingsScreenKey.ROOT.screenTitle) }
+ navController.addOnDestinationChangedListener { _, _, arguments ->
+ screenTitle =
+ arguments?.getInt(SCREEN_TITLE_KEY) ?: SettingsScreenKey.ROOT.screenTitle
+ }
+
+ AppTheme {
+ Scaffold(topBar = {
+ Toolbar(
+ title = stringResource(id = screenTitle),
+ hasSearch = true,
+ onSearchQueryChange = null // TODO: Add suggestions logic
+ )
+ }) { padding ->
+ NavHost(
+ navController = navController,
+ startDestination = SettingsScreenKey.ROOT.name,
+ modifier = Modifier.padding(padding)
+ ) {
+ composable(
+ SettingsScreenKey.ROOT.name,
+ listOf(createScreenTitleArg(SettingsScreenKey.ROOT.screenTitle))
+ ) {
+ SettingsScreen(onSelectSettingOption = { screen ->
+ navController.navigate(screen.name)
+ })
+ }
+ composable(
+ SettingsScreenKey.DEBUG.name,
+ listOf(createScreenTitleArg(SettingsScreenKey.DEBUG.screenTitle))
+ ) {
+ DebugScreen(settingsViewModel)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+fun createScreenTitleArg(@StringRes screenTitle: Int) = navArgument(SCREEN_TITLE_KEY) {
+ defaultValue = screenTitle
+}
+
+enum class SettingsScreenKey(@StringRes val screenTitle: Int) {
+ ROOT(R.string.settings),
+ DEBUG(R.string.settings_category_debug_title)
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt
new file mode 100644
index 000000000..ae3520c94
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt
@@ -0,0 +1,39 @@
+package org.schabi.newpipe.settings.viewmodel
+
+import android.app.Application
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.AndroidViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import org.schabi.newpipe.R
+import javax.inject.Inject
+
+@HiltViewModel
+class SettingsViewModel @Inject constructor(
+ @ApplicationContext context: Context,
+ private val preferenceManager: SharedPreferences
+) : AndroidViewModel(context.applicationContext as Application) {
+
+ private var _settingsLayoutRedesignPref: Boolean
+ get() = preferenceManager.getBoolean(
+ ContextCompat.getString(getApplication(), R.string.settings_layout_redesign_key), false
+ )
+ set(value) {
+ preferenceManager.edit().putBoolean(
+ ContextCompat.getString(getApplication(), R.string.settings_layout_redesign_key),
+ value
+ ).apply()
+ }
+ private val _settingsLayoutRedesign: MutableStateFlow =
+ MutableStateFlow(_settingsLayoutRedesignPref)
+ val settingsLayoutRedesign = _settingsLayoutRedesign.asStateFlow()
+
+ fun toggleSettingsLayoutRedesign(newState: Boolean) {
+ _settingsLayoutRedesign.value = newState
+ _settingsLayoutRedesignPref = newState
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt b/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt
new file mode 100644
index 000000000..d479343f5
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt
@@ -0,0 +1,53 @@
+package org.schabi.newpipe.ui
+
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import org.schabi.newpipe.ui.theme.SizeTokens
+
+@Composable
+fun SwitchPreference(
+ modifier: Modifier = Modifier,
+ @StringRes title: Int,
+ isChecked: Boolean,
+ onCheckedChange: (Boolean) -> Unit,
+ @StringRes summary: Int? = null
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = modifier.fillMaxWidth()
+ ) {
+ Column {
+ Text(
+ text = stringResource(id = title),
+ modifier = Modifier.padding(SizeTokens.SpacingExtraSmall),
+ style = MaterialTheme.typography.titleSmall,
+ textAlign = TextAlign.Start,
+ )
+ summary?.let {
+ Text(
+ text = stringResource(id = summary),
+ modifier = Modifier.padding(SizeTokens.SpacingExtraSmall),
+ style = MaterialTheme.typography.bodySmall,
+ textAlign = TextAlign.Start,
+ )
+ }
+ }
+ Spacer(modifier = Modifier.width(SizeTokens.SpacingSmall))
+ Switch(checked = isChecked, onCheckedChange = onCheckedChange)
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt b/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt
new file mode 100644
index 000000000..f58f2f305
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt
@@ -0,0 +1,66 @@
+package org.schabi.newpipe.ui
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import org.schabi.newpipe.ui.theme.SizeTokens
+
+@Composable
+fun TextPreference(
+ modifier: Modifier = Modifier,
+ @StringRes title: Int,
+ @DrawableRes icon: Int? = null,
+ @StringRes summary: Int? = null,
+ onClick: () -> Unit,
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(SizeTokens.SpacingSmall)
+ .defaultMinSize(minHeight = SizeTokens.SpaceMinSize)
+ .clickable { onClick() }
+ ) {
+ icon?.let {
+ Icon(
+ painter = painterResource(id = icon),
+ contentDescription = "icon for $title preference"
+ )
+ Spacer(modifier = Modifier.width(SizeTokens.SpacingSmall))
+ }
+ Column {
+ Text(
+ text = stringResource(id = title),
+ modifier = Modifier.padding(SizeTokens.SpacingExtraSmall),
+ style = MaterialTheme.typography.titleSmall,
+ textAlign = TextAlign.Start,
+ )
+ summary?.let {
+ Text(
+ text = stringResource(id = summary),
+ modifier = Modifier.padding(SizeTokens.SpacingExtraSmall),
+ style = MaterialTheme.typography.bodySmall,
+ textAlign = TextAlign.Start,
+ )
+ }
+ }
+ }
+}
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 534d7085b..219328875 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -21,6 +21,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import androidx.preference.PreferenceManager;
import com.jakewharton.processphoenix.ProcessPhoenix;
@@ -64,6 +65,7 @@ import org.schabi.newpipe.player.helper.PlayerHolder;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.settings.SettingsActivity;
+import org.schabi.newpipe.settings.SettingsV2Activity;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.List;
@@ -648,7 +650,13 @@ public final class NavigationHelper {
}
public static void openSettings(final Context context) {
- final Intent intent = new Intent(context, SettingsActivity.class);
+ final Class> settingsClass = PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(
+ ContextCompat.getString(context, R.string.settings_layout_redesign_key),
+ false
+ ) ? SettingsV2Activity.class : SettingsActivity.class;
+
+ final Intent intent = new Intent(context, settingsClass);
context.startActivity(intent);
}
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index e31cebb92..b0fceb89b 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -246,6 +246,7 @@
crash_the_app_key
show_error_snackbar_key
create_error_notification_key
+ settings_layout_redesign_key
theme
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 938a2497d..eecce7988 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -492,6 +492,7 @@
Crash the app
Show an error snackbar
Create an error notification
+ Enable the Redesigned Settings page
Import
Import from
diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml
index d97c5aa1a..5b6909892 100644
--- a/app/src/main/res/xml/debug_settings.xml
+++ b/app/src/main/res/xml/debug_settings.xml
@@ -64,4 +64,11 @@
android:title="@string/create_error_notification"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
+
+
diff --git a/build.gradle b/build.gradle
index 1acfb6f4a..7249be503 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
From 7787eafd3ae7652e2b1f66912d05ea4a7cdca8b9 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:03:08 +0200
Subject: [PATCH 66/93] Fix build failing locally due to outdated kotlin
version
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 7249be503..2d03416a7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '2.0.0'
+ ext.kotlin_version = '2.0.21'
repositories {
google()
mavenCentral()
From d8759993a98cde7ebfab2968cc7473d693d43cbf Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 20:57:21 +0200
Subject: [PATCH 67/93] CI: Use Java 21
---
.github/workflows/ci.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9ae3a77c2..54415858e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,10 +47,10 @@ jobs:
BRANCH: ${{ github.head_ref }}
run: git checkout -B "$BRANCH"
- - name: set up JDK 17
+ - name: set up JDK
uses: actions/setup-java@v4
with:
- java-version: 17
+ java-version: 21
distribution: "temurin"
cache: 'gradle'
@@ -88,10 +88,10 @@ jobs:
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- - name: set up JDK 17
+ - name: set up JDK
uses: actions/setup-java@v4
with:
- java-version: 17
+ java-version: 21
distribution: "temurin"
cache: 'gradle'
@@ -121,10 +121,10 @@ jobs:
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- - name: Set up JDK 17
+ - name: Set up JDK
uses: actions/setup-java@v4
with:
- java-version: 17
+ java-version: 21
distribution: "temurin"
cache: 'gradle'
From 702f74291d46eec2544d4a45ae267709f5f4b253 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:03:33 +0200
Subject: [PATCH 68/93] Use working Extractor version The tag can't be resolved
by Jitpack so use the commit-hash instead
---
app/build.gradle | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 8f7036e03..2aef7d275 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -205,7 +205,8 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
- implementation 'com.github.teamnewpipe:newpipeextractor:v0.24.2'
+ // WORKAROUND: v0.24.2 can't be resolved by jitpack -> use git commit hash instead
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:176da72cb4c3ec4679211339b0e59f6b01bf2f52'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
From 4de3ef20be878b0cdac1c6d14135731106b1fd63 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:18:06 +0200
Subject: [PATCH 69/93] Delete symlink
---
app/src/main/res/layout-land/list_stream_card_item.xml | 1 -
1 file changed, 1 deletion(-)
delete mode 120000 app/src/main/res/layout-land/list_stream_card_item.xml
diff --git a/app/src/main/res/layout-land/list_stream_card_item.xml b/app/src/main/res/layout-land/list_stream_card_item.xml
deleted file mode 120000
index 70228ee1d..000000000
--- a/app/src/main/res/layout-land/list_stream_card_item.xml
+++ /dev/null
@@ -1 +0,0 @@
-../layout/list_stream_item.xml
\ No newline at end of file
From 6dd24033a48df40ce7105784f46c4a4445666858 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:18:34 +0200
Subject: [PATCH 70/93] Replace symlink with original
Co-Authored-By: Thompson3142 <115718208+thompson3142@users.noreply.github.com>
---
.../res/layout-land/list_stream_card_item.xml | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 app/src/main/res/layout-land/list_stream_card_item.xml
diff --git a/app/src/main/res/layout-land/list_stream_card_item.xml b/app/src/main/res/layout-land/list_stream_card_item.xml
new file mode 100644
index 000000000..793942568
--- /dev/null
+++ b/app/src/main/res/layout-land/list_stream_card_item.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 97652ac0150a6e5170cd54cf02eca0635125ad7a Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:39:44 +0200
Subject: [PATCH 71/93] Update Gradle to latest version
---
build.gradle | 2 +-
gradle/wrapper/gradle-wrapper.properties | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/build.gradle b/build.gradle
index 2d03416a7..25568c220 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.2.0'
+ classpath 'com.android.tools.build:gradle:8.7.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51.1'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d022615ff..4ea536e77 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 5bd4ed77dfd30592febfbaea192509dab54461be Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Tue, 22 Oct 2024 21:40:16 +0200
Subject: [PATCH 72/93] Fix Android Gradle plugin warning
---
app/build.gradle | 1 +
gradle.properties | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2aef7d275..51909c6eb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -95,6 +95,7 @@ android {
buildFeatures {
viewBinding true
compose true
+ buildConfig true
}
packagingOptions {
diff --git a/gradle.properties b/gradle.properties
index 0ca913222..ed32303da 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,3 @@
-android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=false
android.nonFinalResIds=false
android.nonTransitiveRClass=true
From 8ada566bf16bcafb57a1d6920f5610e156b5a495 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Wed, 23 Oct 2024 21:28:07 +0200
Subject: [PATCH 73/93] Replaced ``Icepick`` with ``Bridge`` and
``Android-State``
* IcePick fails on Java 21 (default in Android Studio 2024.2)
* Bridge is the most modern alternative that is currently available. It is backed by ``Android-State`` and can be configured with various frameworks
* In the long term this should be replaced with something better
---
app/build.gradle | 7 ++-
app/proguard-rules.pro | 9 ---
app/src/main/java/org/schabi/newpipe/App.java | 2 +
.../java/org/schabi/newpipe/BaseFragment.java | 9 +--
.../org/schabi/newpipe/RouterActivity.java | 9 +--
.../org/schabi/newpipe/about/AboutActivity.kt | 8 ++-
.../newpipe/download/DownloadDialog.java | 12 ++--
.../newpipe/fragments/BaseStateFragment.java | 5 +-
.../fragments/detail/DescriptionFragment.java | 6 +-
.../fragments/detail/VideoDetailFragment.java | 2 +-
.../fragments/list/BaseListInfoFragment.java | 5 +-
.../list/channel/ChannelAboutFragment.java | 6 +-
.../list/channel/ChannelFragment.java | 12 ++--
.../list/channel/ChannelTabFragment.java | 6 +-
.../list/comments/CommentRepliesFragment.java | 3 +-
.../fragments/list/kiosk/KioskFragment.java | 3 +-
.../fragments/list/search/SearchFragment.java | 7 ++-
.../local/bookmark/BookmarkFragment.java | 7 ++-
.../schabi/newpipe/local/feed/FeedFragment.kt | 2 +-
.../history/StatisticsPlaylistFragment.java | 3 +-
.../local/playlist/LocalPlaylistFragment.java | 8 ++-
.../ImportConfirmationDialog.java | 14 ++---
.../subscription/SubscriptionFragment.kt | 2 +-
.../SubscriptionsImportFragment.java | 4 +-
.../subscription/dialog/FeedGroupDialog.kt | 8 +--
.../dialog/FeedGroupReorderDialog.kt | 12 ++--
.../helper/PlaybackParameterDialog.java | 10 +--
.../newpipe/settings/SettingsActivity.java | 9 ++-
.../util/BridgeStateSaverInitializer.java | 61 +++++++++++++++++++
.../schabi/newpipe/views/CollapsibleView.java | 16 ++---
30 files changed, 167 insertions(+), 100 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java
diff --git a/app/build.gradle b/app/build.gradle
index 51909c6eb..b32109692 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -116,7 +116,7 @@ ext {
androidxRoomVersion = '2.6.1'
androidxWorkVersion = '2.8.1'
- icepickVersion = '3.2.0'
+ stateSaverVersion = '1.4.1'
exoPlayerVersion = '2.18.7'
googleAutoServiceVersion = '1.1.1'
groupieVersion = '2.10.1'
@@ -243,8 +243,9 @@ dependencies {
/** Third-party libraries **/
// Instance state boilerplate elimination
- implementation "frankiesardo:icepick:${icepickVersion}"
- kapt "frankiesardo:icepick-processor:${icepickVersion}"
+ implementation 'com.github.livefront:bridge:v2.0.2'
+ implementation "com.evernote:android-state:$stateSaverVersion"
+ kapt "com.evernote:android-state-processor:$stateSaverVersion"
// HTML parser
implementation "org.jsoup:jsoup:1.17.2"
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 435c4e29b..215df0da5 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -13,15 +13,6 @@
## Rules for ExoPlayer
-keep class com.google.android.exoplayer2.** { *; }
-## Rules for Icepick. Copy pasted from https://github.com/frankiesardo/icepick
--dontwarn icepick.**
--keep class icepick.** { *; }
--keep class **$$Icepick { *; }
--keepclasseswithmembernames class * {
- @icepick.* ;
-}
--keepnames class * { @icepick.State *;}
-
## Rules for OkHttp. Copy pasted from https://github.com/square/okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index f1876c5dd..d271dba0d 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -21,6 +21,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.settings.NewPipeSettings;
+import org.schabi.newpipe.util.BridgeStateSaverInitializer;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
@@ -107,6 +108,7 @@ public class App extends Application implements ImageLoaderFactory {
Localization.getPreferredContentCountry(this));
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
+ BridgeStateSaverInitializer.init(this);
StateSaver.init(this);
initNotificationChannels();
diff --git a/app/src/main/java/org/schabi/newpipe/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/BaseFragment.java
index 7a06771dd..a55a341e6 100644
--- a/app/src/main/java/org/schabi/newpipe/BaseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/BaseFragment.java
@@ -10,8 +10,9 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
-import icepick.Icepick;
-import icepick.State;
+import com.evernote.android.state.State;
+import com.livefront.bridge.Bridge;
+
public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
@@ -48,7 +49,7 @@ public abstract class BaseFragment extends Fragment {
+ "savedInstanceState = [" + savedInstanceState + "]");
}
super.onCreate(savedInstanceState);
- Icepick.restoreInstanceState(this, savedInstanceState);
+ Bridge.restoreInstanceState(this, savedInstanceState);
if (savedInstanceState != null) {
onRestoreInstanceState(savedInstanceState);
}
@@ -70,7 +71,7 @@ public abstract class BaseFragment extends Fragment {
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
- Icepick.saveInstanceState(this, outState);
+ Bridge.saveInstanceState(this, outState);
}
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index c59dc7532..197c965ba 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -41,6 +41,9 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceManager;
+import com.evernote.android.state.State;
+import com.livefront.bridge.Bridge;
+
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
@@ -98,8 +101,6 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
-import icepick.Icepick;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
@@ -152,7 +153,7 @@ public class RouterActivity extends AppCompatActivity {
getWindow().setAttributes(params);
super.onCreate(savedInstanceState);
- Icepick.restoreInstanceState(this, savedInstanceState);
+ Bridge.restoreInstanceState(this, savedInstanceState);
// FragmentManager will take care to recreate (Playlist|Download)Dialog when screen rotates
// We used to .setOnDismissListener(dialog -> finish()); when creating these DialogFragments
@@ -197,7 +198,7 @@ public class RouterActivity extends AppCompatActivity {
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
- Icepick.saveInstanceState(this, outState);
+ Bridge.saveInstanceState(this, outState);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
index 0d0d0d48d..60a1cff37 100644
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
+++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
@@ -138,8 +138,12 @@ class AboutActivity : AppCompatActivity() {
"https://github.com/lisawray/groupie", StandardLicenses.MIT
),
SoftwareComponent(
- "Icepick", "2015", "Frankie Sardo",
- "https://github.com/frankiesardo/icepick", StandardLicenses.EPL1
+ "Android-State", "2018", "Evernote",
+ "https://github.com/Evernote/android-state", StandardLicenses.EPL1
+ ),
+ SoftwareComponent(
+ "Bridge", "2021", "Livefront",
+ "https://github.com/livefront/bridge", StandardLicenses.APACHE2
),
SoftwareComponent(
"Jsoup", "2009 - 2020", "Jonathan Hedley",
diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
index db2066b27..34a4ba022 100644
--- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
@@ -39,6 +39,8 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
+import com.evernote.android.state.State;
+import com.livefront.bridge.Bridge;
import com.nononsenseapps.filepicker.Utils;
import org.schabi.newpipe.MainActivity;
@@ -59,6 +61,8 @@ import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
import org.schabi.newpipe.streams.io.StoredDirectoryHelper;
import org.schabi.newpipe.streams.io.StoredFileHelper;
+import org.schabi.newpipe.util.AudioTrackAdapter;
+import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.FilenameUtils;
import org.schabi.newpipe.util.ListHelper;
@@ -67,8 +71,6 @@ import org.schabi.newpipe.util.SecondaryStreamHelper;
import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper;
-import org.schabi.newpipe.util.AudioTrackAdapter;
-import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.File;
@@ -79,8 +81,6 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
-import icepick.Icepick;
-import icepick.State;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import us.shandian.giga.get.MissionRecoveryInfo;
import us.shandian.giga.postprocessing.Postprocessing;
@@ -214,7 +214,7 @@ public class DownloadDialog extends DialogFragment
context = getContext();
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
- Icepick.restoreInstanceState(this, savedInstanceState);
+ Bridge.restoreInstanceState(this, savedInstanceState);
this.audioTrackAdapter = new AudioTrackAdapter(wrappedAudioTracks);
this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams);
@@ -372,7 +372,7 @@ public class DownloadDialog extends DialogFragment
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
- Icepick.saveInstanceState(this, outState);
+ Bridge.saveInstanceState(this, outState);
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
index a3d3d8b60..8361953b9 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java
@@ -13,6 +13,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
@@ -22,8 +24,6 @@ import org.schabi.newpipe.util.InfoCache;
import java.util.concurrent.atomic.AtomicBoolean;
-import icepick.State;
-
public abstract class BaseStateFragment extends BaseFragment implements ViewContract {
@State
protected AtomicBoolean wasLoading = new AtomicBoolean();
@@ -134,6 +134,7 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC
hideErrorPanel();
}
+ @Override
public void showEmptyState() {
isLoading.set(false);
if (emptyStateView != null) {
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
index 581e54156..52fb3f29e 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java
@@ -11,6 +11,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream.Description;
@@ -19,8 +21,6 @@ import org.schabi.newpipe.util.Localization;
import java.util.List;
-import icepick.State;
-
public class DescriptionFragment extends BaseDescriptionFragment {
@State
@@ -31,7 +31,7 @@ public class DescriptionFragment extends BaseDescriptionFragment {
}
public DescriptionFragment() {
- // keep empty constructor for IcePick when resuming fragment from memory
+ // keep empty constructor for State when resuming fragment from memory
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 11a315d69..33f489d9d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -56,6 +56,7 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;
+import com.evernote.android.state.State;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.material.appbar.AppBarLayout;
@@ -128,7 +129,6 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import coil.util.CoilUtils;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
index dd5eb6c8a..61a361f23 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
@@ -9,6 +9,8 @@ import android.view.View;
import androidx.annotation.NonNull;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
@@ -24,7 +26,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable;
@@ -143,7 +144,7 @@ public abstract class BaseListInfoFragment {
+ .subscribe((@NonNull final L result) -> {
isLoading.set(false);
currentInfo = result;
currentNextPage = result.getNextPage();
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java
index 0dc2fb65a..b7f4a9d3d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java
@@ -10,6 +10,8 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
@@ -20,8 +22,6 @@ import org.schabi.newpipe.util.Localization;
import java.util.List;
-import icepick.State;
-
public class ChannelAboutFragment extends BaseDescriptionFragment {
@State
protected ChannelInfo channelInfo;
@@ -31,7 +31,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment {
}
public ChannelAboutFragment() {
- // keep empty constructor for IcePick when resuming fragment from memory
+ // keep empty constructor for State when resuming fragment from memory
}
@Override
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 3890e4865..56d8a9315 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
@@ -25,6 +25,7 @@ import androidx.core.graphics.ColorUtils;
import androidx.core.view.MenuProvider;
import androidx.preference.PreferenceManager;
+import com.evernote.android.state.State;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import com.jakewharton.rxbinding4.view.RxView;
@@ -60,7 +61,6 @@ import java.util.Queue;
import java.util.concurrent.TimeUnit;
import coil.util.CoilUtils;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
@@ -249,7 +249,7 @@ public class ChannelFragment extends BaseStateFragment
//////////////////////////////////////////////////////////////////////////*/
private void monitorSubscription(final ChannelInfo info) {
- final Consumer onError = (Throwable throwable) -> {
+ final Consumer onError = (final Throwable throwable) -> {
animate(binding.channelSubscribeButton, false, 100);
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET,
"Get subscription status", currentInfo));
@@ -284,14 +284,14 @@ public class ChannelFragment extends BaseStateFragment
}
private Function mapOnSubscribe(final SubscriptionEntity subscription) {
- return (@NonNull Object o) -> {
+ return (@NonNull final Object o) -> {
subscriptionManager.insertSubscription(subscription);
return o;
};
}
private Function mapOnUnsubscribe(final SubscriptionEntity subscription) {
- return (@NonNull Object o) -> {
+ return (@NonNull final Object o) -> {
subscriptionManager.deleteSubscription(subscription);
return o;
};
@@ -318,7 +318,7 @@ public class ChannelFragment extends BaseStateFragment
}
private Disposable monitorSubscribeButton(final Function action) {
- final Consumer onNext = (@NonNull Object o) -> {
+ final Consumer onNext = (@NonNull final Object o) -> {
if (DEBUG) {
Log.d(TAG, "Changed subscription status to this channel!");
}
@@ -338,7 +338,7 @@ public class ChannelFragment extends BaseStateFragment
}
private Consumer> getSubscribeUpdateMonitor(final ChannelInfo info) {
- return (List subscriptionEntities) -> {
+ return (final List subscriptionEntities) -> {
if (DEBUG) {
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: "
+ "subscriptionEntities = [" + subscriptionEntities + "]");
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java
index 95ac42eed..5d398821a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java
@@ -9,6 +9,8 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.PlaylistControlBinding;
import org.schabi.newpipe.error.UserAction;
@@ -32,13 +34,12 @@ import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import icepick.State;
import io.reactivex.rxjava3.core.Single;
public class ChannelTabFragment extends BaseListInfoFragment
implements PlaylistControlViewHolder {
- // states must be protected and not private for IcePick being able to access them
+ // states must be protected and not private for State being able to access them
@State
protected ListLinkHandler tabHandler;
@State
@@ -156,6 +157,7 @@ public class ChannelTabFragment extends BaseListInfoFragment streamItems = infoListAdapter.getItemsList().stream()
.filter(StreamInfoItem.class::isInstance)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java
index 4eb73520f..a585a5b39 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java
@@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.text.HtmlCompat;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.CommentRepliesHeaderBinding;
import org.schabi.newpipe.error.UserAction;
@@ -30,7 +32,6 @@ import org.schabi.newpipe.util.text.TextLinkifier;
import java.util.Queue;
import java.util.function.Supplier;
-import icepick.State;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
index b90dccb17..6823e13d3 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java
@@ -11,6 +11,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
@@ -29,7 +31,6 @@ import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization;
-import icepick.State;
import io.reactivex.rxjava3.core.Single;
/**
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index eef3455ae..18c60400b 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -40,6 +40,8 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
+import com.evernote.android.state.State;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentSearchBinding;
import org.schabi.newpipe.error.ErrorInfo;
@@ -77,7 +79,6 @@ import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
@@ -550,7 +551,7 @@ public class SearchFragment extends BaseListFragment {
+ searchEditText.setOnFocusChangeListener((final View v, final boolean hasFocus) -> {
if (DEBUG) {
Log.d(TAG, "onFocusChange() called with: "
+ "v = [" + v + "], hasFocus = [" + hasFocus + "]");
@@ -611,7 +612,7 @@ public class SearchFragment extends BaseListFragment {
+ (final TextView v, final int actionId, final KeyEvent event) -> {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], "
+ "actionId = [" + actionId + "], event = [" + event + "]");
diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java
index a366723e0..a5e1594d1 100644
--- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java
@@ -19,6 +19,8 @@ import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
+import com.evernote.android.state.State;
+
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@@ -36,16 +38,15 @@ import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder;
import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
-import org.schabi.newpipe.util.debounce.DebounceSavable;
-import org.schabi.newpipe.util.debounce.DebounceSaver;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
+import org.schabi.newpipe.util.debounce.DebounceSavable;
+import org.schabi.newpipe.util.debounce.DebounceSaver;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
index b99291309..a4e53aab1 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
@@ -44,11 +44,11 @@ import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.evernote.android.state.State
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.OnItemClickListener
import com.xwray.groupie.OnItemLongClickListener
-import icepick.State
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
index 1fea7e155..fac358075 100644
--- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java
@@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewbinding.ViewBinding;
+import com.evernote.android.state.State;
import com.google.android.material.snackbar.Snackbar;
import org.reactivestreams.Subscriber;
@@ -45,7 +46,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
@@ -368,6 +368,7 @@ public class StatisticsPlaylistFragment
}
}
+ @Override
public PlayQueue getPlayQueue() {
return getPlayQueue(0);
}
diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
index d5ae431fa..c87d9cccc 100644
--- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
@@ -26,6 +26,8 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
+import com.evernote.android.state.State;
+
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@@ -49,12 +51,12 @@ import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
-import org.schabi.newpipe.util.debounce.DebounceSavable;
-import org.schabi.newpipe.util.debounce.DebounceSaver;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.PlayButtonHelper;
+import org.schabi.newpipe.util.debounce.DebounceSavable;
+import org.schabi.newpipe.util.debounce.DebounceSaver;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.ArrayList;
@@ -63,7 +65,6 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
-import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
@@ -843,6 +844,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment) {
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
index dfb49a25b..7e74c3848 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java
@@ -24,6 +24,9 @@ import androidx.core.math.MathUtils;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
+import com.evernote.android.state.State;
+import com.livefront.bridge.Bridge;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.DialogPlaybackParameterBinding;
import org.schabi.newpipe.player.ui.VideoPlayerUi;
@@ -37,9 +40,6 @@ import java.util.function.DoubleConsumer;
import java.util.function.DoubleFunction;
import java.util.function.DoubleSupplier;
-import icepick.Icepick;
-import icepick.State;
-
public class PlaybackParameterDialog extends DialogFragment {
private static final String TAG = "PlaybackParameterDialog";
@@ -135,7 +135,7 @@ public class PlaybackParameterDialog extends DialogFragment {
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
- Icepick.saveInstanceState(this, outState);
+ Bridge.saveInstanceState(this, outState);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -146,7 +146,7 @@ public class PlaybackParameterDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
assureCorrectAppLanguage(getContext());
- Icepick.restoreInstanceState(this, savedInstanceState);
+ Bridge.restoreInstanceState(this, savedInstanceState);
binding = DialogPlaybackParameterBinding.inflate(getLayoutInflater());
initUI();
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 529e53442..0d57ce174 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -21,7 +21,9 @@ import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import com.evernote.android.state.State;
import com.jakewharton.rxbinding4.widget.RxTextView;
+import com.livefront.bridge.Bridge;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
@@ -41,9 +43,6 @@ import org.schabi.newpipe.views.FocusOverlayView;
import java.util.concurrent.TimeUnit;
-import icepick.Icepick;
-import icepick.State;
-
/*
* Created by Christian Schabesberger on 31.08.15.
*
@@ -93,7 +92,7 @@ public class SettingsActivity extends AppCompatActivity implements
assureCorrectAppLanguage(this);
super.onCreate(savedInstanceBundle);
- Icepick.restoreInstanceState(this, savedInstanceBundle);
+ Bridge.restoreInstanceState(this, savedInstanceBundle);
final boolean restored = savedInstanceBundle != null;
final SettingsLayoutBinding settingsLayoutBinding =
@@ -125,7 +124,7 @@ public class SettingsActivity extends AppCompatActivity implements
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
- Icepick.saveInstanceState(this, outState);
+ Bridge.saveInstanceState(this, outState);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java b/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java
new file mode 100644
index 000000000..aeda4717c
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java
@@ -0,0 +1,61 @@
+package org.schabi.newpipe.util;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.evernote.android.state.StateSaver;
+import com.livefront.bridge.Bridge;
+import com.livefront.bridge.SavedStateHandler;
+import com.livefront.bridge.ViewSavedStateHandler;
+
+/**
+ * Configures Bridge's state saver.
+ */
+public final class BridgeStateSaverInitializer {
+
+ public static void init(final Context context) {
+ Bridge.initialize(
+ context,
+ new SavedStateHandler() {
+ @Override
+ public void saveInstanceState(
+ @NonNull final Object target,
+ @NonNull final Bundle state) {
+ StateSaver.saveInstanceState(target, state);
+ }
+
+ @Override
+ public void restoreInstanceState(
+ @NonNull final Object target,
+ @Nullable final Bundle state) {
+ StateSaver.restoreInstanceState(target, state);
+ }
+ },
+ new ViewSavedStateHandler() {
+ @NonNull
+ @Override
+ public Parcelable saveInstanceState(
+ @NonNull final T target,
+ @Nullable final Parcelable parentState) {
+ return StateSaver.saveInstanceState(target, parentState);
+ }
+
+ @Nullable
+ @Override
+ public Parcelable restoreInstanceState(
+ @NonNull final T target,
+ @Nullable final Parcelable state) {
+ return StateSaver.restoreInstanceState(target, state);
+ }
+ }
+ );
+ }
+
+ private BridgeStateSaverInitializer() {
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java
index f79e1e3a3..91b5ebd07 100644
--- a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java
+++ b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java
@@ -19,6 +19,9 @@
package org.schabi.newpipe.views;
+import static org.schabi.newpipe.MainActivity.DEBUG;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Parcelable;
@@ -29,18 +32,15 @@ import android.widget.LinearLayout;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import com.evernote.android.state.State;
+import com.livefront.bridge.Bridge;
+
import org.schabi.newpipe.ktx.ViewUtils;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
-import icepick.Icepick;
-import icepick.State;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-import static org.schabi.newpipe.MainActivity.DEBUG;
-
/**
* A view that can be fully collapsed and expanded.
*/
@@ -207,12 +207,12 @@ public class CollapsibleView extends LinearLayout {
@Nullable
@Override
public Parcelable onSaveInstanceState() {
- return Icepick.saveInstanceState(this, super.onSaveInstanceState());
+ return Bridge.saveInstanceState(this, super.onSaveInstanceState());
}
@Override
public void onRestoreInstanceState(final Parcelable state) {
- super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
+ super.onRestoreInstanceState(Bridge.restoreInstanceState(this, state));
ready();
}
From 36ede243e3118ae0745f87cba913afa36fe46aa7 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sun, 10 Nov 2024 20:53:49 +0100
Subject: [PATCH 74/93] Update compose bom and fix navigation compose without
version
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 264a9db6e..1cd2ede51 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -294,7 +294,7 @@ dependencies {
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
// Jetpack Compose
- implementation(platform('androidx.compose:compose-bom:2024.09.03'))
+ implementation(platform('androidx.compose:compose-bom:2024.10.01'))
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.activity:activity-compose'
@@ -304,7 +304,7 @@ dependencies {
// Jetpack Compose related dependencies
implementation 'androidx.paging:paging-compose:3.3.2'
- implementation "androidx.navigation:navigation-compose"
+ implementation "androidx.navigation:navigation-compose:2.8.3"
// Coroutines interop
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.8.1'
From e6b13412467d166cbdefc6273fff2186b28c69e7 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sun, 10 Nov 2024 23:07:55 +0100
Subject: [PATCH 75/93] Improve Comment layout
---
.../ui/components/video/comment/Comment.kt | 138 +++++++++++++-----
1 file changed, 104 insertions(+), 34 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index baf0c7106..f4412ab23 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -15,11 +15,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -36,7 +38,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import org.schabi.newpipe.R
@@ -71,7 +73,7 @@ fun Comment(comment: CommentsInfoItem) {
},
onClick = { isExpanded = !isExpanded }
)
- .padding(8.dp),
+ .padding(start = 8.dp, top = 10.dp, end = 8.dp, bottom = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
AsyncImage(
@@ -80,6 +82,7 @@ fun Comment(comment: CommentsInfoItem) {
placeholder = painterResource(R.drawable.placeholder_person),
error = painterResource(R.drawable.placeholder_person),
modifier = Modifier
+ .padding(vertical = 4.dp)
.size(42.dp)
.clip(CircleShape)
.clickable {
@@ -87,12 +90,15 @@ fun Comment(comment: CommentsInfoItem) {
}
)
- Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
- Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
+ Column {
+ Row(verticalAlignment = Alignment.CenterVertically) {
if (comment.isPinned) {
Image(
painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description),
+ modifier = Modifier
+ .padding(start = 1.dp, end = 4.dp)
+ .size(20.dp)
)
}
@@ -102,7 +108,12 @@ fun Comment(comment: CommentsInfoItem) {
)
Localization.concatenateStrings(comment.uploaderName, date)
}
- Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
+ Text(
+ text = nameAndDate,
+ style = MaterialTheme.typography.titleSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
}
Text(
@@ -111,35 +122,56 @@ fun Comment(comment: CommentsInfoItem) {
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(top = 6.dp)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(start = 1.dp, top = 6.dp, end = 4.dp, bottom = 6.dp)
+ ) {
Image(
painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ contentDescription = stringResource(R.string.detail_likes_img_view_description),
+ modifier = Modifier
+ .padding(end = 4.dp)
+ .size(20.dp),
+ )
+ Text(
+ text = Localization.likeCount(context, comment.likeCount),
+ maxLines = 1,
+ style = MaterialTheme.typography.labelMedium,
)
- Text(text = Localization.likeCount(context, comment.likeCount))
if (comment.isHeartedByUploader) {
Image(
painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ contentDescription = stringResource(R.string.detail_heart_img_view_description),
+ modifier = Modifier
+ .padding(start = 8.dp)
+ .size(20.dp),
)
}
}
if (comment.replies != null) {
- TextButton(onClick = { showReplies = true }) {
- val text = pluralStringResource(
- R.plurals.replies, comment.replyCount, comment.replyCount.toString()
- )
- Text(text = text)
+ // reduce LocalMinimumInteractiveComponentSize from 48dp to 44dp to slightly
+ // reduce the button margin (which is still clickable but not visible)
+ CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 44.dp) {
+ TextButton(
+ onClick = { showReplies = true },
+ modifier = Modifier.padding(end = 2.dp)
+ ) {
+ val text = pluralStringResource(
+ R.plurals.replies, comment.replyCount, comment.replyCount.toString()
+ )
+ Text(text = text)
+ }
}
}
}
@@ -174,32 +206,70 @@ fun CommentsInfoItem(
this.replyCount = replyCount
}
-private class DescriptionPreviewProvider : PreviewParameterProvider {
- override val values = sequenceOf(
- Description("Hello world! This line should be hidden by default.", Description.HTML),
- Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
+private class CommentPreviewProvider : CollectionPreviewParameterProvider(
+ listOf(
+ CommentsInfoItem(
+ commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
+ uploaderName = "Test",
+ likeCount = 100,
+ isPinned = false,
+ isHeartedByUploader = true,
+ replies = null,
+ replyCount = 0
+ ),
+ CommentsInfoItem(
+ commentText = Description("Hello world, long long long text lorem ipsum dolor sit amet! This line should be hidden by default.", Description.HTML),
+ uploaderName = "Test",
+ likeCount = 92847,
+ isPinned = true,
+ isHeartedByUploader = false,
+ replies = Page(""),
+ replyCount = 10
+ ),
+ CommentsInfoItem(
+ commentText = Description("Hello world, long long long text lorem ipsum dolor sit amet! This line should be hidden by default.", Description.HTML),
+ uploaderName = "Test really long long long long lorem ipsum dolor sit amet consectetur",
+ likeCount = 92847,
+ isPinned = true,
+ isHeartedByUploader = true,
+ replies = null,
+ replyCount = 0
+ ),
+ CommentsInfoItem(
+ commentText = Description("Short comment", Description.HTML),
+ uploaderName = "Test really long long long long lorem ipsum dolor sit amet consectetur",
+ likeCount = 92847,
+ isPinned = false,
+ isHeartedByUploader = false,
+ replies = Page(""),
+ replyCount = 4283
+ ),
)
-}
+)
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CommentPreview(
- @PreviewParameter(DescriptionPreviewProvider::class) description: Description
+ @PreviewParameter(CommentPreviewProvider::class) commentsInfoItem: CommentsInfoItem
) {
- val comment = CommentsInfoItem(
- commentText = description,
- uploaderName = "Test",
- likeCount = 100,
- isPinned = true,
- isHeartedByUploader = true,
- replies = Page(""),
- replyCount = 10
- )
-
AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
- Comment(comment)
+ Comment(commentsInfoItem)
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun CommentListPreview() {
+ AppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ Column {
+ for (comment in CommentPreviewProvider().values) {
+ Comment(comment)
+ }
+ }
}
}
}
From 802a0941548391280af3e826072559b0a80ac7f3 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sun, 10 Nov 2024 23:28:59 +0100
Subject: [PATCH 76/93] Improve comment replies dialog layout
---
.../video/comment/CommentRepliesDialog.kt | 63 +++++++++----------
.../video/comment/CommentRepliesHeader.kt | 33 ++++++----
2 files changed, 53 insertions(+), 43 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 30131d91e..33eedd0b4 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -6,9 +6,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
-import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -65,41 +63,42 @@ private fun CommentRepliesDialog(
val state = rememberLazyListState()
ModalBottomSheet(onDismissRequest = onDismissRequest) {
- Surface(color = MaterialTheme.colorScheme.background) {
- LazyColumnScrollbar(
- state = state,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
+ ) {
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
) {
- LazyColumn(
- modifier = Modifier.nestedScroll(nestedScrollInterop),
- state = state
- ) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(thickness = 1.dp)
- }
+ item {
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(
+ thickness = 1.dp,
+ modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 8.dp)
+ )
+ }
- if (comments.itemCount == 0) {
- item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ if (comments.itemCount == 0) {
+ item {
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val message = if (refresh is LoadState.Error) {
+ R.string.error_unable_to_load_comments
} else {
- val message = if (refresh is LoadState.Error) {
- R.string.error_unable_to_load_comments
- } else {
- R.string.no_comments
- }
- NoItemsMessage(message)
+ R.string.no_comments
}
+ NoItemsMessage(message)
}
- } else {
- items(comments.itemCount) {
- Comment(comment = comments[it]!!)
- }
+ }
+ } else {
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!)
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 3a10fe021..6025a5df4 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
@@ -43,11 +44,13 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
verticalAlignment = Alignment.CenterVertically
) {
Row(
- modifier = Modifier.clickable {
- NavigationHelper.openCommentAuthorIfPresent(context, comment)
- },
+ modifier = Modifier
+ .padding(end = 12.dp)
+ .clip(CircleShape)
+ .clickable { NavigationHelper.openCommentAuthorIfPresent(context, comment) }
+ .weight(1.0f, true),
horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
AsyncImage(
model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars),
@@ -60,14 +63,18 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
)
Column {
- Text(text = comment.uploaderName)
+ Text(
+ text = comment.uploaderName,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = MaterialTheme.typography.titleSmall,
+ )
Text(
- color = MaterialTheme.colorScheme.secondary,
- style = MaterialTheme.typography.bodySmall,
text = Localization.relativeTimeOrTextual(
context, comment.uploadDate, comment.textualUploadDate
- )
+ ),
+ style = MaterialTheme.typography.bodySmall,
)
}
}
@@ -80,7 +87,10 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
painter = painterResource(R.drawable.ic_thumb_up),
contentDescription = stringResource(R.string.detail_likes_img_view_description)
)
- Text(text = Localization.likeCount(context, comment.likeCount))
+ Text(
+ text = Localization.likeCount(context, comment.likeCount),
+ maxLines = 1,
+ )
if (comment.isHeartedByUploader) {
Image(
@@ -100,7 +110,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
DescriptionText(
description = comment.commentText,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.padding(8.dp)
)
}
}
@@ -111,7 +122,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
fun CommentRepliesHeaderPreview() {
val comment = CommentsInfoItem(
commentText = Description("Hello world!", Description.PLAIN_TEXT),
- uploaderName = "Test",
+ uploaderName = "Test really long lorem ipsum dolor sit",
likeCount = 1000,
isPinned = true,
isHeartedByUploader = true
From 412e1d602a03a17bbcc7c5fd1e89895a68293cd1 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sun, 10 Nov 2024 23:45:10 +0100
Subject: [PATCH 77/93] Better handle unknown values for comment & like count
---
.../ui/components/video/comment/Comment.kt | 32 ++++++++++---------
.../video/comment/CommentRepliesDialog.kt | 17 +++++++++-
.../video/comment/CommentRepliesHeader.kt | 19 ++++++-----
.../video/comment/CommentSection.kt | 18 +++++++----
4 files changed, 55 insertions(+), 31 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index f4412ab23..498ee1898 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -135,26 +135,28 @@ fun Comment(comment: CommentsInfoItem) {
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 1.dp, top = 6.dp, end = 4.dp, bottom = 6.dp)
) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description),
- modifier = Modifier
- .padding(end = 4.dp)
- .size(20.dp),
- )
- Text(
- text = Localization.likeCount(context, comment.likeCount),
- maxLines = 1,
- style = MaterialTheme.typography.labelMedium,
- )
+ // do not show anything if the like count is unknown
+ if (comment.likeCount >= 0) {
+ Image(
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description),
+ modifier = Modifier
+ .padding(end = 4.dp)
+ .size(20.dp),
+ )
+ Text(
+ text = Localization.likeCount(context, comment.likeCount),
+ maxLines = 1,
+ style = MaterialTheme.typography.labelMedium,
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ }
if (comment.isHeartedByUploader) {
Image(
painter = painterResource(R.drawable.ic_heart),
contentDescription = stringResource(R.string.detail_heart_img_view_description),
- modifier = Modifier
- .padding(start = 8.dp)
- .size(20.dp),
+ modifier = Modifier.size(20.dp),
)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 33eedd0b4..5e8ce1c4a 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -6,7 +6,9 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -14,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
@@ -78,7 +81,7 @@ private fun CommentRepliesDialog(
CommentRepliesHeader(comment = parentComment)
HorizontalDivider(
thickness = 1.dp,
- modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 8.dp)
+ modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
)
}
@@ -97,6 +100,18 @@ private fun CommentRepliesDialog(
}
}
} else {
+ if (comments.itemCount >= 0) {
+ item {
+ Text(
+ modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
+ text = pluralStringResource(
+ R.plurals.replies, comments.itemCount, comments.itemCount
+ ),
+ maxLines = 1,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
+ }
items(comments.itemCount) {
Comment(comment = comments[it]!!)
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 6025a5df4..c987a774d 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -83,14 +83,17 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
- )
- Text(
- text = Localization.likeCount(context, comment.likeCount),
- maxLines = 1,
- )
+ // do not show anything if the like count is unknown
+ if (comment.likeCount >= 0) {
+ Image(
+ painter = painterResource(R.drawable.ic_thumb_up),
+ contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ )
+ Text(
+ text = Localization.likeCount(context, comment.likeCount),
+ maxLines = 1,
+ )
+ }
if (comment.isHeartedByUploader) {
Image(
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index f643af69d..118231764 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -14,7 +14,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -84,12 +83,17 @@ private fun CommentSection(
NoItemsMessage(R.string.no_comments)
}
} else {
- item {
- Text(
- modifier = Modifier.padding(start = 8.dp),
- text = pluralStringResource(R.plurals.comments, count, count),
- fontWeight = FontWeight.Bold
- )
+ // do not show anything if the comment count is unknown
+ if (count >= 0) {
+ item {
+ Text(
+ modifier = Modifier
+ .padding(start = 12.dp, end = 12.dp, bottom = 4.dp),
+ text = pluralStringResource(R.plurals.comments, count, count),
+ maxLines = 1,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
}
when (comments.loadState.refresh) {
From 23b3835af015d76c97985fbcd6326216d45156fb Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 00:16:32 +0100
Subject: [PATCH 78/93] Fix PagingSource for comments
The previous implementation was skipping the first page of comments
---
.../newpipe/paging/CommentRepliesSource.kt | 27 ++++++++++++++
.../schabi/newpipe/paging/CommentsSource.kt | 35 ++++++-------------
.../video/comment/CommentRepliesDialog.kt | 7 ++--
3 files changed, 41 insertions(+), 28 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/paging/CommentRepliesSource.kt
diff --git a/app/src/main/java/org/schabi/newpipe/paging/CommentRepliesSource.kt b/app/src/main/java/org/schabi/newpipe/paging/CommentRepliesSource.kt
new file mode 100644
index 000000000..efa189db9
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/paging/CommentRepliesSource.kt
@@ -0,0 +1,27 @@
+package org.schabi.newpipe.paging
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.schabi.newpipe.extractor.NewPipe
+import org.schabi.newpipe.extractor.Page
+import org.schabi.newpipe.extractor.comments.CommentsInfo
+import org.schabi.newpipe.extractor.comments.CommentsInfoItem
+
+class CommentRepliesSource(
+ private val commentInfo: CommentsInfoItem,
+) : PagingSource() {
+ private val service = NewPipe.getService(commentInfo.serviceId)
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ // params.key is null the first time load() is called, and we need to return the first page
+ val repliesPage = params.key ?: commentInfo.replies
+ val info = withContext(Dispatchers.IO) {
+ CommentsInfo.getMoreItems(service, commentInfo.url, repliesPage)
+ }
+ return LoadResult.Page(info.items, null, info.nextPage)
+ }
+
+ override fun getRefreshKey(state: PagingState) = null
+}
diff --git a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
index d92589732..669485e66 100644
--- a/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
+++ b/app/src/main/java/org/schabi/newpipe/paging/CommentsSource.kt
@@ -9,35 +9,20 @@ import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfo
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.ui.components.video.comment.CommentInfo
-import org.schabi.newpipe.util.NO_SERVICE_ID
-class CommentsSource(
- serviceId: Int,
- private val url: String,
- private val repliesPage: Page?,
- private val commentInfo: CommentInfo? = null,
-) : PagingSource() {
- constructor(commentInfo: CommentInfo) : this(
- commentInfo.serviceId, commentInfo.url, commentInfo.nextPage, commentInfo
- )
-
- init {
- require(serviceId != NO_SERVICE_ID) { "serviceId is NO_SERVICE_ID" }
- }
- private val service = NewPipe.getService(serviceId)
+class CommentsSource(private val commentInfo: CommentInfo) : PagingSource() {
+ private val service = NewPipe.getService(commentInfo.serviceId)
override suspend fun load(params: LoadParams): LoadResult {
- // repliesPage is non-null only when used to load the comment replies
- val nextKey = params.key ?: repliesPage
-
- return withContext(Dispatchers.IO) {
- nextKey?.let {
- val info = CommentsInfo.getMoreItems(service, url, it)
- LoadResult.Page(info.items, null, info.nextPage)
- } ?: run {
- val info = commentInfo ?: CommentInfo(CommentsInfo.getInfo(service, url))
- LoadResult.Page(info.comments, null, info.nextPage)
+ // params.key is null the first time the load() function is called, so we need to return the
+ // first batch of already-loaded comments
+ if (params.key == null) {
+ return LoadResult.Page(commentInfo.comments, null, commentInfo.nextPage)
+ } else {
+ val info = withContext(Dispatchers.IO) {
+ CommentsInfo.getMoreItems(service, commentInfo.url, params.key)
}
+ return LoadResult.Page(info.items, null, info.nextPage)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 5e8ce1c4a..80d5a86ee 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -32,7 +32,7 @@ import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
-import org.schabi.newpipe.paging.CommentsSource
+import org.schabi.newpipe.paging.CommentRepliesSource
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
@@ -46,8 +46,9 @@ fun CommentRepliesDialog(
val coroutineScope = rememberCoroutineScope()
val commentsFlow = remember {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
- CommentsSource(parentComment.serviceId, parentComment.url, parentComment.replies)
- }.flow
+ CommentRepliesSource(parentComment)
+ }
+ .flow
.cachedIn(coroutineScope)
}
From ef56dea8175684be6775677fbf784017ee104335 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 00:29:29 +0100
Subject: [PATCH 79/93] Fix content color in comment replies fragment
---
.../video/comment/CommentRepliesDialog.kt | 97 +++++++++++--------
1 file changed, 55 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 80d5a86ee..94217e43f 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -6,10 +6,13 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
+import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -67,54 +70,64 @@ private fun CommentRepliesDialog(
val state = rememberLazyListState()
ModalBottomSheet(onDismissRequest = onDismissRequest) {
- LazyColumnScrollbar(
- state = state,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
+ CompositionLocalProvider(
+ // contentColorFor(MaterialTheme.colorScheme.containerColor), i.e. ModalBottomSheet's
+ // default background color, does not resolve correctly, so need to manually set the
+ // content color for MaterialTheme.colorScheme.background instead
+ LocalContentColor provides contentColorFor(MaterialTheme.colorScheme.background)
) {
- LazyColumn(
- modifier = Modifier.nestedScroll(nestedScrollInterop),
- state = state
+ LazyColumnScrollbar(
+ state = state,
+ settings = ScrollbarSettings.Default.copy(
+ thumbSelectedColor = md_theme_dark_primary,
+ thumbUnselectedColor = Color.Red
+ )
) {
- item {
- CommentRepliesHeader(comment = parentComment)
- HorizontalDivider(
- thickness = 1.dp,
- modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
- )
- }
-
- if (comments.itemCount == 0) {
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
+ ) {
item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else {
- val message = if (refresh is LoadState.Error) {
- R.string.error_unable_to_load_comments
- } else {
- R.string.no_comments
- }
- NoItemsMessage(message)
- }
+ CommentRepliesHeader(comment = parentComment)
+ HorizontalDivider(
+ thickness = 1.dp,
+ modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
+ )
}
- } else {
- if (comments.itemCount >= 0) {
+
+ if (comments.itemCount == 0) {
item {
- Text(
- modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
- text = pluralStringResource(
- R.plurals.replies, comments.itemCount, comments.itemCount
- ),
- maxLines = 1,
- style = MaterialTheme.typography.titleMedium
- )
+ val refresh = comments.loadState.refresh
+ if (refresh is LoadState.Loading) {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ } else {
+ val message = if (refresh is LoadState.Error) {
+ R.string.error_unable_to_load_comments
+ } else {
+ R.string.no_comments
+ }
+ NoItemsMessage(message)
+ }
+ }
+ } else {
+ if (comments.itemCount >= 0) {
+ item {
+ Text(
+ modifier = Modifier.padding(
+ horizontal = 12.dp,
+ vertical = 4.dp
+ ),
+ text = pluralStringResource(
+ R.plurals.replies, comments.itemCount, comments.itemCount
+ ),
+ maxLines = 1,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
+ }
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!)
}
- }
- items(comments.itemCount) {
- Comment(comment = comments[it]!!)
}
}
}
From 9d8a79b0bd4d12376bf5ba1a930a6fae86be3113 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 13:34:18 +0100
Subject: [PATCH 80/93] Slightly improve comment replies header spacing
---
.../components/video/comment/CommentRepliesDialog.kt | 10 +++++++---
.../components/video/comment/CommentRepliesHeader.kt | 6 +++---
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 94217e43f..8cff6b864 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -21,6 +21,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.Pager
@@ -146,10 +147,13 @@ private fun CommentRepliesDialogPreview() {
isPinned = true,
isHeartedByUploader = true
)
- val replies = (1..10).map {
+ val replies = (1..10).map { i ->
CommentsInfoItem(
- commentText = Description("Reply $it", Description.PLAIN_TEXT),
- uploaderName = "Test"
+ commentText = Description(
+ "Reply $i: ${LoremIpsum(i * i).values.first()}",
+ Description.PLAIN_TEXT,
+ ),
+ uploaderName = LoremIpsum(11 - i).values.first()
)
}
val flow = flowOf(PagingData.from(replies))
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index c987a774d..97682561b 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -22,6 +22,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import org.schabi.newpipe.R
@@ -37,7 +38,7 @@ import org.schabi.newpipe.util.image.ImageStrategy
fun CommentRepliesHeader(comment: CommentsInfoItem) {
val context = LocalContext.current
- Column(modifier = Modifier.padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
+ Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
@@ -114,7 +115,6 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
DescriptionText(
description = comment.commentText,
style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(8.dp)
)
}
}
@@ -124,7 +124,7 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
@Composable
fun CommentRepliesHeaderPreview() {
val comment = CommentsInfoItem(
- commentText = Description("Hello world!", Description.PLAIN_TEXT),
+ commentText = Description(LoremIpsum(50).values.first(), Description.PLAIN_TEXT),
uploaderName = "Test really long lorem ipsum dolor sit",
likeCount = 1000,
isPinned = true,
From 800961c3d791070f8b90191d3a28ec85dab9a724 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 13:51:24 +0100
Subject: [PATCH 81/93] Unexpand bottom sheet dialog when clicking on a channel
---
.../ui/components/video/comment/Comment.kt | 13 +++++--
.../video/comment/CommentRepliesDialog.kt | 39 +++++++++++++++----
.../video/comment/CommentRepliesHeader.kt | 14 +++++--
.../video/comment/CommentSection.kt | 2 +-
4 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index 498ee1898..d10749267 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -53,7 +53,7 @@ import org.schabi.newpipe.util.image.ImageStrategy
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun Comment(comment: CommentsInfoItem) {
+fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
var isExpanded by rememberSaveable { mutableStateOf(false) }
@@ -87,6 +87,7 @@ fun Comment(comment: CommentsInfoItem) {
.clip(CircleShape)
.clickable {
NavigationHelper.openCommentAuthorIfPresent(context, comment)
+ onCommentAuthorOpened()
}
)
@@ -181,7 +182,11 @@ fun Comment(comment: CommentsInfoItem) {
}
if (showReplies) {
- CommentRepliesDialog(comment, onDismissRequest = { showReplies = false })
+ CommentRepliesDialog(
+ parentComment = comment,
+ onDismissRequest = { showReplies = false },
+ onCommentAuthorOpened = onCommentAuthorOpened,
+ )
}
}
@@ -257,7 +262,7 @@ private fun CommentPreview(
) {
AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
- Comment(commentsInfoItem)
+ Comment(commentsInfoItem) {}
}
}
}
@@ -269,7 +274,7 @@ private fun CommentListPreview() {
Surface(color = MaterialTheme.colorScheme.background) {
Column {
for (comment in CommentPreviewProvider().values) {
- Comment(comment)
+ Comment(comment) {}
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 8cff6b864..6ef43bec8 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -11,6 +11,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
@@ -31,6 +32,7 @@ import androidx.paging.cachedIn
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.launch
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
@@ -46,6 +48,7 @@ import org.schabi.newpipe.ui.theme.md_theme_dark_primary
fun CommentRepliesDialog(
parentComment: CommentsInfoItem,
onDismissRequest: () -> Unit,
+ onCommentAuthorOpened: () -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val commentsFlow = remember {
@@ -56,7 +59,7 @@ fun CommentRepliesDialog(
.cachedIn(coroutineScope)
}
- CommentRepliesDialog(parentComment, commentsFlow, onDismissRequest)
+ CommentRepliesDialog(parentComment, commentsFlow, onDismissRequest, onCommentAuthorOpened)
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -65,12 +68,26 @@ private fun CommentRepliesDialog(
parentComment: CommentsInfoItem,
commentsFlow: Flow>,
onDismissRequest: () -> Unit,
+ onCommentAuthorOpened: () -> Unit,
) {
val comments = commentsFlow.collectAsLazyPagingItems()
val nestedScrollInterop = rememberNestedScrollInteropConnection()
- val state = rememberLazyListState()
+ val listState = rememberLazyListState()
- ModalBottomSheet(onDismissRequest = onDismissRequest) {
+ val coroutineScope = rememberCoroutineScope()
+ val sheetState = rememberModalBottomSheetState()
+ val nestedOnCommentAuthorOpened: () -> Unit = {
+ // also partialExpand any parent dialog
+ onCommentAuthorOpened()
+ coroutineScope.launch {
+ sheetState.partialExpand()
+ }
+ }
+
+ ModalBottomSheet(
+ sheetState = sheetState,
+ onDismissRequest = onDismissRequest,
+ ) {
CompositionLocalProvider(
// contentColorFor(MaterialTheme.colorScheme.containerColor), i.e. ModalBottomSheet's
// default background color, does not resolve correctly, so need to manually set the
@@ -78,7 +95,7 @@ private fun CommentRepliesDialog(
LocalContentColor provides contentColorFor(MaterialTheme.colorScheme.background)
) {
LazyColumnScrollbar(
- state = state,
+ state = listState,
settings = ScrollbarSettings.Default.copy(
thumbSelectedColor = md_theme_dark_primary,
thumbUnselectedColor = Color.Red
@@ -86,10 +103,13 @@ private fun CommentRepliesDialog(
) {
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
- state = state
+ state = listState
) {
item {
- CommentRepliesHeader(comment = parentComment)
+ CommentRepliesHeader(
+ comment = parentComment,
+ onCommentAuthorOpened = nestedOnCommentAuthorOpened,
+ )
HorizontalDivider(
thickness = 1.dp,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
@@ -127,7 +147,10 @@ private fun CommentRepliesDialog(
}
}
items(comments.itemCount) {
- Comment(comment = comments[it]!!)
+ Comment(
+ comment = comments[it]!!,
+ onCommentAuthorOpened = nestedOnCommentAuthorOpened,
+ )
}
}
}
@@ -159,6 +182,6 @@ private fun CommentRepliesDialogPreview() {
val flow = flowOf(PagingData.from(replies))
AppTheme {
- CommentRepliesDialog(comment, flow, onDismissRequest = {})
+ CommentRepliesDialog(comment, flow, onDismissRequest = {}, onCommentAuthorOpened = {})
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 97682561b..c1ddf61cb 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -35,10 +35,13 @@ import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.image.ImageStrategy
@Composable
-fun CommentRepliesHeader(comment: CommentsInfoItem) {
+fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
val context = LocalContext.current
- Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
@@ -48,7 +51,10 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
modifier = Modifier
.padding(end = 12.dp)
.clip(CircleShape)
- .clickable { NavigationHelper.openCommentAuthorIfPresent(context, comment) }
+ .clickable {
+ NavigationHelper.openCommentAuthorIfPresent(context, comment)
+ onCommentAuthorOpened()
+ }
.weight(1.0f, true),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -133,7 +139,7 @@ fun CommentRepliesHeaderPreview() {
AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
- CommentRepliesHeader(comment)
+ CommentRepliesHeader(comment) {}
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index 118231764..cd0a6271c 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -111,7 +111,7 @@ private fun CommentSection(
else -> {
items(comments.itemCount) {
- Comment(comment = comments[it]!!)
+ Comment(comment = comments[it]!!) {}
}
}
}
From a92a28517ec4e3cd4d13ec47d6db1dc9ff622e4c Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 14:25:12 +0100
Subject: [PATCH 82/93] Use Icons.Default.* instead of vector assets
---
app/build.gradle | 1 +
.../items/playlist/PlaylistThumbnail.kt | 11 ++++----
.../ui/components/video/comment/Comment.kt | 21 ++++++++++------
.../video/comment/CommentRepliesHeader.kt | 25 +++++++++++--------
4 files changed, 35 insertions(+), 23 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1cd2ede51..24e3d32cf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -301,6 +301,7 @@ dependencies {
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
implementation 'androidx.compose.ui:ui-text' // Needed for parsing HTML to AnnotatedString
+ implementation 'androidx.compose.material:material-icons-extended'
// Jetpack Compose related dependencies
implementation 'androidx.paging:paging-compose:3.3.2'
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
index 3fc139119..b1d4d200c 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
@@ -1,18 +1,19 @@
package org.schabi.newpipe.ui.components.items.playlist
-import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@@ -46,10 +47,10 @@ fun PlaylistThumbnail(
.padding(2.dp),
verticalAlignment = Alignment.CenterVertically
) {
- Image(
- painter = painterResource(R.drawable.ic_playlist_play),
+ Icon(
+ imageVector = Icons.AutoMirrored.Default.PlaylistPlay,
contentDescription = null,
- colorFilter = ColorFilter.tint(Color.White),
+ tint = Color.White,
modifier = Modifier.size(18.dp)
)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index d10749267..ca6114a88 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -5,7 +5,6 @@ import android.os.Build
import android.widget.Toast
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -15,6 +14,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.PushPin
+import androidx.compose.material.icons.filled.ThumbUp
+import androidx.compose.material3.Icon
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -94,11 +98,11 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
Column {
Row(verticalAlignment = Alignment.CenterVertically) {
if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
+ Icon(
+ imageVector = Icons.Default.PushPin,
contentDescription = stringResource(R.string.detail_pinned_comment_view_description),
modifier = Modifier
- .padding(start = 1.dp, end = 4.dp)
+ .padding(end = 3.dp)
.size(20.dp)
)
}
@@ -138,8 +142,8 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
) {
// do not show anything if the like count is unknown
if (comment.likeCount >= 0) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
+ Icon(
+ imageVector = Icons.Default.ThumbUp,
contentDescription = stringResource(R.string.detail_likes_img_view_description),
modifier = Modifier
.padding(end = 4.dp)
@@ -154,9 +158,10 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
}
if (comment.isHeartedByUploader) {
- Image(
- painter = painterResource(R.drawable.ic_heart),
+ Icon(
+ imageVector = Icons.Default.Favorite,
contentDescription = stringResource(R.string.detail_heart_img_view_description),
+ tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp),
)
}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index c1ddf61cb..95542092e 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -1,7 +1,6 @@
package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
-import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -10,6 +9,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.PushPin
+import androidx.compose.material.icons.filled.ThumbUp
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@@ -92,9 +96,9 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () ->
) {
// do not show anything if the like count is unknown
if (comment.likeCount >= 0) {
- Image(
- painter = painterResource(R.drawable.ic_thumb_up),
- contentDescription = stringResource(R.string.detail_likes_img_view_description)
+ Icon(
+ imageVector = Icons.Default.ThumbUp,
+ contentDescription = stringResource(R.string.detail_likes_img_view_description),
)
Text(
text = Localization.likeCount(context, comment.likeCount),
@@ -103,16 +107,17 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, onCommentAuthorOpened: () ->
}
if (comment.isHeartedByUploader) {
- Image(
- painter = painterResource(R.drawable.ic_heart),
- contentDescription = stringResource(R.string.detail_heart_img_view_description)
+ Icon(
+ imageVector = Icons.Default.Favorite,
+ contentDescription = stringResource(R.string.detail_heart_img_view_description),
+ tint = MaterialTheme.colorScheme.primary,
)
}
if (comment.isPinned) {
- Image(
- painter = painterResource(R.drawable.ic_pin),
- contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
+ Icon(
+ imageVector = Icons.Default.PushPin,
+ contentDescription = stringResource(R.string.detail_pinned_comment_view_description),
)
}
}
From cea149f8528655fea7ffd9cdc821de609172c312 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 14:26:01 +0100
Subject: [PATCH 83/93] Add .kotlin/ to gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 1352b6917..7bccc3132 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ captures/
*.class
app/debug/
app/release/
+.kotlin/
# vscode / eclipse files
*.classpath
From 37d1c784fa8d1aac63682fad12cc8629b9514a19 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 14:52:44 +0100
Subject: [PATCH 84/93] Create utilities to copy to clipboard in Compose code
---
.../ui/components/video/comment/Comment.kt | 15 ++--------
.../external_communication/ShareUtilsKt.kt | 28 +++++++++++++++++++
2 files changed, 31 insertions(+), 12 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtilsKt.kt
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index ca6114a88..7f29269d1 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -1,8 +1,6 @@
package org.schabi.newpipe.ui.components.video.comment
import android.content.res.Configuration
-import android.os.Build
-import android.widget.Toast
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
@@ -34,7 +32,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@@ -53,12 +50,12 @@ import org.schabi.newpipe.ui.components.common.rememberParsedDescription
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.NavigationHelper
+import org.schabi.newpipe.util.external_communication.copyToClipboardCallback
import org.schabi.newpipe.util.image.ImageStrategy
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
- val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
var isExpanded by rememberSaveable { mutableStateOf(false) }
var showReplies by rememberSaveable { mutableStateOf(false) }
@@ -68,14 +65,8 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
modifier = Modifier
.animateContentSize()
.combinedClickable(
- onLongClick = {
- clipboardManager.setText(parsedDescription)
- if (Build.VERSION.SDK_INT < 33) {
- // Android 13 has its own "copied to clipboard" dialog
- Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show()
- }
- },
- onClick = { isExpanded = !isExpanded }
+ onLongClick = copyToClipboardCallback { parsedDescription },
+ onClick = { isExpanded = !isExpanded },
)
.padding(start = 8.dp, top = 10.dp, end = 8.dp, bottom = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtilsKt.kt b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtilsKt.kt
new file mode 100644
index 000000000..fd60f348d
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtilsKt.kt
@@ -0,0 +1,28 @@
+package org.schabi.newpipe.util.external_communication
+
+import android.content.Context
+import android.os.Build
+import android.widget.Toast
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.AnnotatedString
+import org.schabi.newpipe.R
+
+fun ClipboardManager.setTextAndShowToast(context: Context, annotatedString: AnnotatedString) {
+ setText(annotatedString)
+ if (Build.VERSION.SDK_INT < 33) {
+ // Android 13 has its own "copied to clipboard" dialog
+ Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show()
+ }
+}
+
+@Composable
+fun copyToClipboardCallback(annotatedString: () -> AnnotatedString): (() -> Unit) {
+ val clipboardManager = LocalClipboardManager.current
+ val context = LocalContext.current
+ return {
+ clipboardManager.setTextAndShowToast(context, annotatedString())
+ }
+}
From aea2b7c7f348f8333ee9bd1d90258938db9864a5 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 14:56:48 +0100
Subject: [PATCH 85/93] Show correct reply count in dialog
---
.../video/comment/CommentRepliesDialog.kt | 33 ++++++++++---------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index 6ef43bec8..ba6ba7517 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -116,6 +116,24 @@ private fun CommentRepliesDialog(
)
}
+ if (parentComment.replyCount >= 0) {
+ item {
+ Text(
+ modifier = Modifier.padding(
+ horizontal = 12.dp,
+ vertical = 4.dp
+ ),
+ text = pluralStringResource(
+ R.plurals.replies,
+ parentComment.replyCount,
+ parentComment.replyCount,
+ ),
+ maxLines = 1,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
+ }
+
if (comments.itemCount == 0) {
item {
val refresh = comments.loadState.refresh
@@ -131,21 +149,6 @@ private fun CommentRepliesDialog(
}
}
} else {
- if (comments.itemCount >= 0) {
- item {
- Text(
- modifier = Modifier.padding(
- horizontal = 12.dp,
- vertical = 4.dp
- ),
- text = pluralStringResource(
- R.plurals.replies, comments.itemCount, comments.itemCount
- ),
- maxLines = 1,
- style = MaterialTheme.typography.titleMedium
- )
- }
- }
items(comments.itemCount) {
Comment(
comment = comments[it]!!,
From fdf36cbad61d5995b87244ae576f2e41b0610f68 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 15:20:38 +0100
Subject: [PATCH 86/93] Deduplicate and improve Scrollbar theme
---
.../newpipe/ui/components/common/Scrollbar.kt | 30 +++++++++++++++++++
.../newpipe/ui/components/items/ItemList.kt | 13 ++------
.../video/comment/CommentRepliesDialog.kt | 13 ++------
.../video/comment/CommentSection.kt | 13 ++------
4 files changed, 36 insertions(+), 33 deletions(-)
create mode 100644 app/src/main/java/org/schabi/newpipe/ui/components/common/Scrollbar.kt
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/Scrollbar.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/Scrollbar.kt
new file mode 100644
index 000000000..eb1595467
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/Scrollbar.kt
@@ -0,0 +1,30 @@
+package org.schabi.newpipe.ui.components.common
+
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import my.nanihadesuka.compose.ScrollbarSettings
+
+@Composable
+fun defaultThemedScrollbarSettings(): ScrollbarSettings = ScrollbarSettings.Default.copy(
+ thumbUnselectedColor = MaterialTheme.colorScheme.primary,
+ thumbSelectedColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.75f),
+)
+
+@Composable
+fun LazyColumnThemedScrollbar(
+ state: LazyListState,
+ modifier: Modifier = Modifier,
+ settings: ScrollbarSettings = defaultThemedScrollbarSettings(),
+ indicatorContent: (@Composable (index: Int, isThumbSelected: Boolean) -> Unit)? = null,
+ content: @Composable () -> Unit
+) {
+ my.nanihadesuka.compose.LazyColumnScrollbar(
+ state = state,
+ modifier = modifier,
+ settings = settings,
+ indicatorContent = indicatorContent,
+ content = content,
+ )
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
index 237a57667..4562e17af 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/ItemList.kt
@@ -10,24 +10,21 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.stringResource
import androidx.preference.PreferenceManager
import androidx.window.core.layout.WindowWidthSizeClass
-import my.nanihadesuka.compose.LazyColumnScrollbar
-import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.InfoItem
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.info_list.ItemViewMode
import org.schabi.newpipe.ktx.findFragmentActivity
+import org.schabi.newpipe.ui.components.common.LazyColumnThemedScrollbar
import org.schabi.newpipe.ui.components.items.playlist.PlaylistListItem
import org.schabi.newpipe.ui.components.items.stream.StreamListItem
-import org.schabi.newpipe.ui.theme.md_theme_dark_primary
import org.schabi.newpipe.util.DependentPreferenceHelper
import org.schabi.newpipe.util.NavigationHelper
@@ -75,13 +72,7 @@ fun ItemList(
} else {
val state = rememberLazyListState()
- LazyColumnScrollbar(
- state = state,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
- ) {
+ LazyColumnThemedScrollbar(state = state) {
LazyColumn(modifier = nestedScrollModifier, state = state) {
listHeader()
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index ba6ba7517..17ea900a5 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -17,7 +17,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
@@ -33,16 +32,14 @@ import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
-import my.nanihadesuka.compose.LazyColumnScrollbar
-import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.paging.CommentRepliesSource
+import org.schabi.newpipe.ui.components.common.LazyColumnThemedScrollbar
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
-import org.schabi.newpipe.ui.theme.md_theme_dark_primary
@Composable
fun CommentRepliesDialog(
@@ -94,13 +91,7 @@ private fun CommentRepliesDialog(
// content color for MaterialTheme.colorScheme.background instead
LocalContentColor provides contentColorFor(MaterialTheme.colorScheme.background)
) {
- LazyColumnScrollbar(
- state = listState,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
- ) {
+ LazyColumnThemedScrollbar(state = listState) {
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
state = listState
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index cd0a6271c..f1740c70a 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -10,7 +10,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
@@ -23,16 +22,14 @@ import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
-import my.nanihadesuka.compose.LazyColumnScrollbar
-import my.nanihadesuka.compose.ScrollbarSettings
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
+import org.schabi.newpipe.ui.components.common.LazyColumnThemedScrollbar
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.common.NoItemsMessage
import org.schabi.newpipe.ui.theme.AppTheme
-import org.schabi.newpipe.ui.theme.md_theme_dark_primary
import org.schabi.newpipe.viewmodels.CommentsViewModel
import org.schabi.newpipe.viewmodels.util.Resource
@@ -52,13 +49,7 @@ private fun CommentSection(
val state = rememberLazyListState()
Surface(color = MaterialTheme.colorScheme.background) {
- LazyColumnScrollbar(
- state = state,
- settings = ScrollbarSettings.Default.copy(
- thumbSelectedColor = md_theme_dark_primary,
- thumbUnselectedColor = Color.Red
- )
- ) {
+ LazyColumnThemedScrollbar(state = state) {
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
state = state
From 13585ca0be1ae808849821b17517440a9c67964c Mon Sep 17 00:00:00 2001
From: Stypox
Date: Mon, 11 Nov 2024 16:15:36 +0100
Subject: [PATCH 87/93] Avoid drawing surface background twice for comments
fragment
---
.../video/comment/CommentSection.kt | 96 +++++++++----------
1 file changed, 47 insertions(+), 49 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index f1740c70a..33c4e2139 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -48,71 +48,69 @@ private fun CommentSection(
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val state = rememberLazyListState()
- Surface(color = MaterialTheme.colorScheme.background) {
- LazyColumnThemedScrollbar(state = state) {
- LazyColumn(
- modifier = Modifier.nestedScroll(nestedScrollInterop),
- state = state
- ) {
- when (uiState) {
- is Resource.Loading -> {
- item {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- }
+ LazyColumnThemedScrollbar(state = state) {
+ LazyColumn(
+ modifier = Modifier.nestedScroll(nestedScrollInterop),
+ state = state
+ ) {
+ when (uiState) {
+ is Resource.Loading -> {
+ item {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
}
+ }
- is Resource.Success -> {
- val commentInfo = uiState.data
- val count = commentInfo.commentCount
+ is Resource.Success -> {
+ val commentInfo = uiState.data
+ val count = commentInfo.commentCount
- if (commentInfo.isCommentsDisabled) {
+ if (commentInfo.isCommentsDisabled) {
+ item {
+ NoItemsMessage(R.string.comments_are_disabled)
+ }
+ } else if (count == 0) {
+ item {
+ NoItemsMessage(R.string.no_comments)
+ }
+ } else {
+ // do not show anything if the comment count is unknown
+ if (count >= 0) {
item {
- NoItemsMessage(R.string.comments_are_disabled)
+ Text(
+ modifier = Modifier
+ .padding(start = 12.dp, end = 12.dp, bottom = 4.dp),
+ text = pluralStringResource(R.plurals.comments, count, count),
+ maxLines = 1,
+ style = MaterialTheme.typography.titleMedium
+ )
}
- } else if (count == 0) {
- item {
- NoItemsMessage(R.string.no_comments)
- }
- } else {
- // do not show anything if the comment count is unknown
- if (count >= 0) {
+ }
+
+ when (comments.loadState.refresh) {
+ is LoadState.Loading -> {
item {
- Text(
- modifier = Modifier
- .padding(start = 12.dp, end = 12.dp, bottom = 4.dp),
- text = pluralStringResource(R.plurals.comments, count, count),
- maxLines = 1,
- style = MaterialTheme.typography.titleMedium
- )
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
}
}
- when (comments.loadState.refresh) {
- is LoadState.Loading -> {
- item {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- }
+ is LoadState.Error -> {
+ item {
+ NoItemsMessage(R.string.error_unable_to_load_comments)
}
+ }
- is LoadState.Error -> {
- item {
- NoItemsMessage(R.string.error_unable_to_load_comments)
- }
- }
-
- else -> {
- items(comments.itemCount) {
- Comment(comment = comments[it]!!) {}
- }
+ else -> {
+ items(comments.itemCount) {
+ Comment(comment = comments[it]!!) {}
}
}
}
}
+ }
- is Resource.Error -> {
- item {
- NoItemsMessage(R.string.error_unable_to_load_comments)
- }
+ is Resource.Error -> {
+ item {
+ NoItemsMessage(R.string.error_unable_to_load_comments)
}
}
}
From 6a98b1dac7d2966daacf3ffe469a874273bef4c4 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 20 Nov 2024 08:44:16 +0530
Subject: [PATCH 88/93] Rename .java to .kt
---
app/src/main/java/org/schabi/newpipe/{App.java => App.kt} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename app/src/main/java/org/schabi/newpipe/{App.java => App.kt} (100%)
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.kt
similarity index 100%
rename from app/src/main/java/org/schabi/newpipe/App.java
rename to app/src/main/java/org/schabi/newpipe/App.kt
From ca855cbca0730e987abfc60061767bc7e06803c1 Mon Sep 17 00:00:00 2001
From: Isira Seneviratne
Date: Wed, 20 Nov 2024 08:44:18 +0530
Subject: [PATCH 89/93] Migrate to Coil 3
---
app/build.gradle | 7 +-
app/src/main/java/org/schabi/newpipe/App.kt | 402 +++++++++---------
.../java/org/schabi/newpipe/MainActivity.java | 4 +-
.../fragments/detail/VideoDetailFragment.java | 2 +-
.../list/channel/ChannelFragment.java | 2 +-
.../list/playlist/PlaylistFragment.java | 2 +-
.../info_list/dialog/InfoItemDialog.java | 2 +-
.../newpipe/local/feed/FeedViewModel.kt | 2 +-
.../org/schabi/newpipe/player/Player.java | 16 +-
.../newpipe/player/helper/PlayerHolder.java | 4 +-
.../SeekbarPreviewThumbnailHolder.java | 2 +-
.../settings/ContentSettingsFragment.java | 4 +-
.../newpipe/settings/NewPipeSettings.java | 2 +-
.../newpipe/settings/SettingMigrations.java | 6 +-
.../items/playlist/PlaylistThumbnail.kt | 2 +-
.../items/stream/StreamThumbnail.kt | 2 +-
.../ui/components/video/comment/Comment.kt | 2 +-
.../video/comment/CommentRepliesHeader.kt | 2 +-
.../org/schabi/newpipe/util/DeviceUtils.java | 4 +-
.../schabi/newpipe/util/ReleaseVersionUtil.kt | 2 +-
.../external_communication/ShareUtils.java | 11 +-
.../schabi/newpipe/util/image/CoilHelper.kt | 149 ++++---
22 files changed, 334 insertions(+), 297 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 24e3d32cf..98270fa70 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -124,6 +124,8 @@ ext {
leakCanaryVersion = '2.12'
stethoVersion = '1.6.0'
+
+ coilVersion = '3.0.3'
}
configurations {
@@ -207,7 +209,7 @@ dependencies {
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
// WORKAROUND: v0.24.2 can't be resolved by jitpack -> use git commit hash instead
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:176da72cb4c3ec4679211339b0e59f6b01bf2f52'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:d3d5f2b3f03a5f2b479b9f6fdf1c2555cbb9de0e'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
@@ -272,7 +274,8 @@ dependencies {
implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}"
// Image loading
- implementation 'io.coil-kt:coil-compose:2.7.0'
+ implementation "io.coil-kt.coil3:coil-compose:${coilVersion}"
+ implementation "io.coil-kt.coil3:coil-network-okhttp:${coilVersion}"
// Markdown library for Android
implementation "io.noties.markwon:core:${markwonVersion}"
diff --git a/app/src/main/java/org/schabi/newpipe/App.kt b/app/src/main/java/org/schabi/newpipe/App.kt
index d271dba0d..85860dc6a 100644
--- a/app/src/main/java/org/schabi/newpipe/App.kt
+++ b/app/src/main/java/org/schabi/newpipe/App.kt
@@ -1,49 +1,43 @@
-package org.schabi.newpipe;
+package org.schabi.newpipe
-import android.app.ActivityManager;
-import android.app.Application;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.core.app.NotificationChannelCompat;
-import androidx.core.app.NotificationManagerCompat;
-import androidx.core.content.ContextCompat;
-import androidx.preference.PreferenceManager;
-
-import com.jakewharton.processphoenix.ProcessPhoenix;
-
-import org.acra.ACRA;
-import org.acra.config.CoreConfigurationBuilder;
-import org.schabi.newpipe.error.ReCaptchaActivity;
-import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.downloader.Downloader;
-import org.schabi.newpipe.ktx.ExceptionUtils;
-import org.schabi.newpipe.settings.NewPipeSettings;
-import org.schabi.newpipe.util.BridgeStateSaverInitializer;
-import org.schabi.newpipe.util.Localization;
-import org.schabi.newpipe.util.ServiceHelper;
-import org.schabi.newpipe.util.StateSaver;
-import org.schabi.newpipe.util.image.ImageStrategy;
-import org.schabi.newpipe.util.image.PreferredImageQuality;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.util.List;
-import java.util.Objects;
-
-import coil.ImageLoader;
-import coil.ImageLoaderFactory;
-import coil.util.DebugLogger;
-import dagger.hilt.android.HiltAndroidApp;
-import io.reactivex.rxjava3.exceptions.CompositeException;
-import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
-import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
-import io.reactivex.rxjava3.exceptions.UndeliverableException;
-import io.reactivex.rxjava3.functions.Consumer;
-import io.reactivex.rxjava3.plugins.RxJavaPlugins;
+import android.app.ActivityManager
+import android.app.Application
+import android.content.Context
+import android.util.Log
+import androidx.core.app.NotificationChannelCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.getSystemService
+import androidx.preference.PreferenceManager
+import coil3.ImageLoader
+import coil3.SingletonImageLoader
+import coil3.request.allowRgb565
+import coil3.request.crossfade
+import coil3.util.DebugLogger
+import com.jakewharton.processphoenix.ProcessPhoenix
+import dagger.hilt.android.HiltAndroidApp
+import io.reactivex.rxjava3.exceptions.CompositeException
+import io.reactivex.rxjava3.exceptions.MissingBackpressureException
+import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException
+import io.reactivex.rxjava3.exceptions.UndeliverableException
+import io.reactivex.rxjava3.functions.Consumer
+import io.reactivex.rxjava3.plugins.RxJavaPlugins
+import org.acra.ACRA.init
+import org.acra.ACRA.isACRASenderServiceProcess
+import org.acra.config.CoreConfigurationBuilder
+import org.schabi.newpipe.error.ReCaptchaActivity
+import org.schabi.newpipe.extractor.NewPipe
+import org.schabi.newpipe.extractor.downloader.Downloader
+import org.schabi.newpipe.ktx.hasAssignableCause
+import org.schabi.newpipe.settings.NewPipeSettings
+import org.schabi.newpipe.util.BridgeStateSaverInitializer
+import org.schabi.newpipe.util.Localization
+import org.schabi.newpipe.util.ServiceHelper
+import org.schabi.newpipe.util.StateSaver
+import org.schabi.newpipe.util.image.ImageStrategy
+import org.schabi.newpipe.util.image.PreferredImageQuality
+import java.io.IOException
+import java.io.InterruptedIOException
+import java.net.SocketException
/*
* Copyright (C) Hans-Christoph Steiner 2016
@@ -62,218 +56,218 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins;
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see .
*/
-
@HiltAndroidApp
-public class App extends Application implements ImageLoaderFactory {
- public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
- private static final String TAG = App.class.toString();
+open class App :
+ Application(),
+ SingletonImageLoader.Factory {
+ var isFirstRun = false
+ private set
- private boolean isFirstRun = false;
- private static App app;
-
- @NonNull
- public static App getApp() {
- return app;
+ override fun attachBaseContext(base: Context?) {
+ super.attachBaseContext(base)
+ initACRA()
}
- @Override
- protected void attachBaseContext(final Context base) {
- super.attachBaseContext(base);
- initACRA();
- }
+ override fun onCreate() {
+ super.onCreate()
- @Override
- public void onCreate() {
- super.onCreate();
-
- app = this;
+ instance = this
if (ProcessPhoenix.isPhoenixProcess(this)) {
- Log.i(TAG, "This is a phoenix process! "
- + "Aborting initialization of App[onCreate]");
- return;
+ Log.i(TAG, "This is a phoenix process! Aborting initialization of App[onCreate]")
+ return
}
// check if the last used preference version is set
// to determine whether this is the first app run
- final int lastUsedPrefVersion = PreferenceManager.getDefaultSharedPreferences(this)
- .getInt(getString(R.string.last_used_preferences_version), -1);
- isFirstRun = lastUsedPrefVersion == -1;
+ val lastUsedPrefVersion =
+ PreferenceManager
+ .getDefaultSharedPreferences(this)
+ .getInt(getString(R.string.last_used_preferences_version), -1)
+ isFirstRun = lastUsedPrefVersion == -1
// Initialize settings first because other initializations can use its values
- NewPipeSettings.initSettings(this);
+ NewPipeSettings.initSettings(this)
- NewPipe.init(getDownloader(),
+ NewPipe.init(
+ getDownloader(),
Localization.getPreferredLocalization(this),
- Localization.getPreferredContentCountry(this));
- Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
+ Localization.getPreferredContentCountry(this),
+ )
+ Localization.initPrettyTime(Localization.resolvePrettyTime(this))
- BridgeStateSaverInitializer.init(this);
- StateSaver.init(this);
- initNotificationChannels();
+ BridgeStateSaverInitializer.init(this)
+ StateSaver.init(this)
+ initNotificationChannels()
- ServiceHelper.initServices(this);
+ ServiceHelper.initServices(this)
// Initialize image loader
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this,
- prefs.getString(getString(R.string.image_quality_key),
- getString(R.string.image_quality_default))));
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
+ ImageStrategy.setPreferredImageQuality(
+ PreferredImageQuality.fromPreferenceKey(
+ this,
+ prefs.getString(
+ getString(R.string.image_quality_key),
+ getString(R.string.image_quality_default),
+ ),
+ ),
+ )
- configureRxJavaErrorHandler();
+ configureRxJavaErrorHandler()
}
- @NonNull
- @Override
- public ImageLoader newImageLoader() {
- return new ImageLoader.Builder(this)
- .allowRgb565(ContextCompat.getSystemService(this, ActivityManager.class)
- .isLowRamDevice())
- .logger(BuildConfig.DEBUG ? new DebugLogger() : null)
- .crossfade(true)
- .build();
+ override fun newImageLoader(context: Context): ImageLoader =
+ ImageLoader
+ .Builder(this)
+ .logger(if (BuildConfig.DEBUG) DebugLogger() else null)
+ .allowRgb565(getSystemService()!!.isLowRamDevice)
+ .crossfade(true)
+ .build()
+
+ protected open fun getDownloader(): Downloader {
+ val downloader = DownloaderImpl.init(null)
+ setCookiesToDownloader(downloader)
+ return downloader
}
- protected Downloader getDownloader() {
- final DownloaderImpl downloader = DownloaderImpl.init(null);
- setCookiesToDownloader(downloader);
- return downloader;
+ protected fun setCookiesToDownloader(downloader: DownloaderImpl) {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
+ val key = getString(R.string.recaptcha_cookies_key)
+ downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, null))
+ downloader.updateYoutubeRestrictedModeCookies(this)
}
- protected void setCookiesToDownloader(final DownloaderImpl downloader) {
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
- getApplicationContext());
- final String key = getApplicationContext().getString(R.string.recaptcha_cookies_key);
- downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, null));
- downloader.updateYoutubeRestrictedModeCookies(getApplicationContext());
- }
-
- private void configureRxJavaErrorHandler() {
+ private fun configureRxJavaErrorHandler() {
// https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling
- RxJavaPlugins.setErrorHandler(new Consumer() {
- @Override
- public void accept(@NonNull final Throwable throwable) {
- Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : "
- + "throwable = [" + throwable.getClass().getName() + "]");
+ RxJavaPlugins.setErrorHandler(
+ object : Consumer {
+ override fun accept(throwable: Throwable) {
+ Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : throwable = [${throwable.javaClass.getName()}]")
- final Throwable actualThrowable;
- if (throwable instanceof UndeliverableException) {
// As UndeliverableException is a wrapper,
// get the cause of it to get the "real" exception
- actualThrowable = Objects.requireNonNull(throwable.getCause());
- } else {
- actualThrowable = throwable;
- }
+ val actualThrowable = (throwable as? UndeliverableException)?.cause ?: throwable
- final List errors;
- if (actualThrowable instanceof CompositeException) {
- errors = ((CompositeException) actualThrowable).getExceptions();
- } else {
- errors = List.of(actualThrowable);
- }
+ val errors = (actualThrowable as? CompositeException)?.exceptions ?: listOf(actualThrowable)
- for (final Throwable error : errors) {
- if (isThrowableIgnored(error)) {
- return;
+ for (error in errors) {
+ if (isThrowableIgnored(error)) {
+ return
+ }
+ if (isThrowableCritical(error)) {
+ reportException(error)
+ return
+ }
}
- if (isThrowableCritical(error)) {
- reportException(error);
- return;
+
+ // Out-of-lifecycle exceptions should only be reported if a debug user wishes so,
+ // When exception is not reported, log it
+ if (isDisposedRxExceptionsReported()) {
+ reportException(actualThrowable)
+ } else {
+ Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", actualThrowable)
}
}
- // Out-of-lifecycle exceptions should only be reported if a debug user wishes so,
- // When exception is not reported, log it
- if (isDisposedRxExceptionsReported()) {
- reportException(actualThrowable);
- } else {
- Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", actualThrowable);
+ fun isThrowableIgnored(throwable: Throwable): Boolean {
+ // Don't crash the application over a simple network problem
+ return throwable // network api cancellation
+ .hasAssignableCause(
+ IOException::class.java,
+ SocketException::class.java, // blocking code disposed
+ InterruptedException::class.java,
+ InterruptedIOException::class.java,
+ )
}
- }
- private boolean isThrowableIgnored(@NonNull final Throwable throwable) {
- // Don't crash the application over a simple network problem
- return ExceptionUtils.hasAssignableCause(throwable,
- // network api cancellation
- IOException.class, SocketException.class,
- // blocking code disposed
- InterruptedException.class, InterruptedIOException.class);
- }
+ fun isThrowableCritical(throwable: Throwable): Boolean {
+ // Though these exceptions cannot be ignored
+ return throwable
+ .hasAssignableCause(
+ NullPointerException::class.java,
+ IllegalArgumentException::class.java, // bug in app
+ OnErrorNotImplementedException::class.java,
+ MissingBackpressureException::class.java,
+ IllegalStateException::class.java,
+ ) // bug in operator
+ }
- private boolean isThrowableCritical(@NonNull final Throwable throwable) {
- // Though these exceptions cannot be ignored
- return ExceptionUtils.hasAssignableCause(throwable,
- NullPointerException.class, IllegalArgumentException.class, // bug in app
- OnErrorNotImplementedException.class, MissingBackpressureException.class,
- IllegalStateException.class); // bug in operator
- }
-
- private void reportException(@NonNull final Throwable throwable) {
- // Throw uncaught exception that will trigger the report system
- Thread.currentThread().getUncaughtExceptionHandler()
- .uncaughtException(Thread.currentThread(), throwable);
- }
- });
+ fun reportException(throwable: Throwable) {
+ // Throw uncaught exception that will trigger the report system
+ Thread.currentThread().uncaughtExceptionHandler
+ .uncaughtException(Thread.currentThread(), throwable)
+ }
+ },
+ )
}
/**
- * Called in {@link #attachBaseContext(Context)} after calling the {@code super} method.
+ * Called in [.attachBaseContext] after calling the `super` method.
* Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA.
*/
- protected void initACRA() {
- if (ACRA.isACRASenderServiceProcess()) {
- return;
+ protected fun initACRA() {
+ if (isACRASenderServiceProcess()) {
+ return
}
- final CoreConfigurationBuilder acraConfig = new CoreConfigurationBuilder()
- .withBuildConfigClass(BuildConfig.class);
- ACRA.init(this, acraConfig);
+ val acraConfig =
+ CoreConfigurationBuilder()
+ .withBuildConfigClass(BuildConfig::class.java)
+ init(this, acraConfig)
}
- private void initNotificationChannels() {
+ private fun initNotificationChannels() {
// Keep the importance below DEFAULT to avoid making noise on every notification update for
// the main and update channels
- final List notificationChannelCompats = List.of(
- new NotificationChannelCompat.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(),
- new NotificationChannelCompat
- .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(),
- new NotificationChannelCompat.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(),
- new NotificationChannelCompat.Builder(getString(R.string.error_report_channel_id),
- NotificationManagerCompat.IMPORTANCE_LOW)
- .setName(getString(R.string.error_report_channel_name))
- .setDescription(getString(R.string.error_report_channel_description))
- .build(),
- 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()
- );
+ val mainChannel = NotificationChannelCompat.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()
+ val appUpdateChannel = NotificationChannelCompat.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()
+ val hashChannel = NotificationChannelCompat.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()
+ val errorReportChannel = NotificationChannelCompat.Builder(
+ getString(R.string.error_report_channel_id),
+ NotificationManagerCompat.IMPORTANCE_LOW,
+ )
+ .setName(getString(R.string.error_report_channel_name))
+ .setDescription(getString(R.string.error_report_channel_description))
+ .build()
+ val newStreamChannel = 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.createNotificationChannelsCompat(notificationChannelCompats);
+ val channels = listOf(mainChannel, appUpdateChannel, hashChannel, errorReportChannel, newStreamChannel)
+
+ NotificationManagerCompat.from(this).createNotificationChannelsCompat(channels)
}
- protected boolean isDisposedRxExceptionsReported() {
- return false;
- }
+ protected open fun isDisposedRxExceptionsReported(): Boolean = false
- public boolean isFirstRun() {
- return isFirstRun;
+ companion object {
+ const val PACKAGE_NAME: String = BuildConfig.APPLICATION_ID
+ private val TAG = App::class.java.toString()
+
+ @JvmStatic
+ lateinit var instance: App
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 41abd10c1..39431537d 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -166,7 +166,7 @@ public class MainActivity extends AppCompatActivity {
NotificationWorker.initialize(this);
}
if (!UpdateSettingsFragment.wasUserAskedForConsent(this)
- && !App.getApp().isFirstRun()
+ && !App.getInstance().isFirstRun()
&& ReleaseVersionUtil.INSTANCE.isReleaseApk()) {
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
}
@@ -176,7 +176,7 @@ public class MainActivity extends AppCompatActivity {
protected void onPostCreate(final Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- final App app = App.getApp();
+ final App app = App.getInstance();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
if (prefs.getBoolean(app.getString(R.string.update_app_key), false)
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 4f5bd9e94..63077e92d 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -127,7 +127,7 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-import coil.util.CoilUtils;
+import coil3.util.CoilUtils;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
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 56d8a9315..55e3ae52a 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
@@ -60,7 +60,7 @@ import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
-import coil.util.CoilUtils;
+import coil3.util.CoilUtils;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
index d4607a9ff..be4f076dd 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java
@@ -62,7 +62,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import coil.util.CoilUtils;
+import coil3.util.CoilUtils;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java
index 0c69557bf..dcf01e190 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java
@@ -346,7 +346,7 @@ public final class InfoItemDialog {
public static void reportErrorDuringInitialization(final Throwable throwable,
final InfoItem item) {
- ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo(
+ ErrorUtil.showSnackbar(App.getInstance().getBaseContext(), new ErrorInfo(
throwable,
UserAction.OPEN_INFO_ITEM_DIALOG,
"none",
diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt
index 728570b17..462e8ef21 100644
--- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt
@@ -165,7 +165,7 @@ class FeedViewModel(
fun getFactory(context: Context, groupId: Long) = viewModelFactory {
initializer {
FeedViewModel(
- App.getApp(),
+ App.instance,
groupId,
// Read initial value from preferences
getShowPlayedItemsFromPreferences(context.applicationContext),
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 74d35cf31..ab5274996 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -46,6 +46,7 @@ import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex;
import static org.schabi.newpipe.util.ListHelper.getResolutionIndex;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static coil3.Image_androidKt.toBitmap;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -53,14 +54,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.core.graphics.drawable.DrawableKt;
import androidx.core.math.MathUtils;
import androidx.preference.PreferenceManager;
@@ -125,7 +124,7 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
-import coil.target.Target;
+import coil3.target.Target;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
@@ -193,7 +192,7 @@ public final class Player implements PlaybackListener, Listener {
@Nullable
private Bitmap currentThumbnail;
@Nullable
- private coil.request.Disposable thumbnailDisposable;
+ private coil3.request.Disposable thumbnailDisposable;
/*//////////////////////////////////////////////////////////////////////////
// Player
@@ -789,27 +788,26 @@ public final class Player implements PlaybackListener, Listener {
// scale down the notification thumbnail for performance
final var thumbnailTarget = new Target() {
@Override
- public void onError(@Nullable final Drawable error) {
+ public void onError(@Nullable final coil3.Image error) {
Log.e(TAG, "Thumbnail - onError() called");
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
onThumbnailLoaded(null);
}
@Override
- public void onStart(@Nullable final Drawable placeholder) {
+ public void onStart(@Nullable final coil3.Image placeholder) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - onStart() called");
}
}
@Override
- public void onSuccess(@NonNull final Drawable result) {
+ public void onSuccess(@NonNull final coil3.Image result) {
if (DEBUG) {
Log.d(TAG, "Thumbnail - onSuccess() called with: drawable = [" + result + "]");
}
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
- onThumbnailLoaded(DrawableKt.toBitmapOrNull(result, result.getIntrinsicWidth(),
- result.getIntrinsicHeight(), null));
+ onThumbnailLoaded(toBitmap(result));
}
};
thumbnailDisposable = CoilHelper.INSTANCE
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java
index b55a6547a..24939c1d8 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java
@@ -16,8 +16,8 @@ import com.google.android.exoplayer2.PlaybackParameters;
import org.schabi.newpipe.App;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.extractor.stream.StreamInfo;
-import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.Player;
+import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
@@ -116,7 +116,7 @@ public final class PlayerHolder {
// helper to handle context in common place as using the same
// context to bind/unbind a service is crucial
private Context getCommonContext() {
- return App.getApp();
+ return App.getInstance();
}
public void startService(final boolean playAfterConnect,
diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java
index d09664aeb..863e2fb8a 100644
--- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java
@@ -179,7 +179,7 @@ public class SeekbarPreviewThumbnailHolder {
// Gets the bitmap within the timeout of 15 seconds imposed by default by OkHttpClient
// Ensure that you are not running on the main thread, otherwise this will hang
- final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(App.getApp(), url);
+ final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(App.getInstance(), url);
if (sw != null) {
Log.d(TAG, "Download of bitmap for seekbarPreview from '" + url + "' took "
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index c47abb930..bb3828265 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -15,7 +15,7 @@ import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PreferredImageQuality;
-import coil.Coil;
+import coil3.SingletonImageLoader;
public class ContentSettingsFragment extends BasePreferenceFragment {
private String youtubeRestrictedModeEnabledKey;
@@ -41,7 +41,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
(preference, newValue) -> {
ImageStrategy.setPreferredImageQuality(PreferredImageQuality
.fromPreferenceKey(requireContext(), (String) newValue));
- final var loader = Coil.imageLoader(preference.getContext());
+ final var loader = SingletonImageLoader.get(preference.getContext());
loader.getMemoryCache().clear();
loader.getDiskCache().clear();
Toast.makeText(preference.getContext(),
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 421440ea7..9fe5240cc 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java
@@ -156,7 +156,7 @@ public final class NewPipeSettings {
prefs.getInt(disabledTunnelingAutomaticallyKey, -1) == 0
&& !prefs.getBoolean(disabledTunnelingKey, false);
- if (App.getApp().isFirstRun()
+ if (App.getInstance().isFirstRun()
|| (wasDeviceBlacklistUpdated && !wasMediaTunnelingEnabledByUser)) {
setMediaTunneling(context);
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java
index d731f2f5e..a77e1c514 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java
@@ -1,5 +1,7 @@
package org.schabi.newpipe.settings;
+import static org.schabi.newpipe.MainActivity.DEBUG;
+
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
@@ -18,8 +20,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import static org.schabi.newpipe.MainActivity.DEBUG;
-
/**
* In order to add a migration, follow these steps, given P is the previous version:
* - in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put in
@@ -171,7 +171,7 @@ public final class SettingMigrations {
final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0);
// no migration to run, already up to date
- if (App.getApp().isFirstRun()) {
+ if (App.getInstance().isFirstRun()) {
sp.edit().putInt(lastPrefVersionKey, VERSION).apply();
return;
} else if (lastPrefVersion == VERSION) {
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
index b1d4d200c..36711105b 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/playlist/PlaylistThumbnail.kt
@@ -18,7 +18,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import coil.compose.AsyncImage
+import coil3.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem
import org.schabi.newpipe.util.Localization
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamThumbnail.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamThumbnail.kt
index bcccd3217..f5515a24a 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamThumbnail.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamThumbnail.kt
@@ -22,7 +22,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
-import coil.compose.AsyncImage
+import coil3.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.Localization
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
index 7f29269d1..efa87b581 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/Comment.kt
@@ -41,7 +41,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
-import coil.compose.AsyncImage
+import coil3.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
index 95542092e..e6627f7f0 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesHeader.kt
@@ -28,7 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.compose.ui.unit.dp
-import coil.compose.AsyncImage
+import coil3.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
diff --git a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
index e9678c2b0..7a357a0c1 100644
--- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
@@ -130,7 +130,7 @@ public final class DeviceUtils {
}
isFireTV =
- App.getApp().getPackageManager().hasSystemFeature(AMAZON_FEATURE_FIRE_TV);
+ App.getInstance().getPackageManager().hasSystemFeature(AMAZON_FEATURE_FIRE_TV);
return isFireTV;
}
@@ -139,7 +139,7 @@ public final class DeviceUtils {
return isTV;
}
- final PackageManager pm = App.getApp().getPackageManager();
+ final PackageManager pm = App.getInstance().getPackageManager();
// from doc: https://developer.android.com/training/tv/start/hardware.html#runtime-check
boolean isTv = ContextCompat.getSystemService(context, UiModeManager.class)
diff --git a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt
index 3ea19fa4f..080f5bace 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt
+++ b/app/src/main/java/org/schabi/newpipe/util/ReleaseVersionUtil.kt
@@ -21,7 +21,7 @@ object ReleaseVersionUtil {
val certificates = mapOf(
RELEASE_CERT_PUBLIC_KEY_SHA256.hexToByteArray() to PackageManager.CERT_INPUT_SHA256
)
- val app = App.getApp()
+ val app = App.instance
try {
PackageInfoCompat.hasSignatures(app.packageManager, app.packageName, certificates, false)
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
index 9008a213d..4be5445bc 100644
--- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
@@ -1,6 +1,7 @@
package org.schabi.newpipe.util.external_communication;
import static org.schabi.newpipe.MainActivity.DEBUG;
+import static coil3.Image_androidKt.toBitmap;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
@@ -31,9 +32,9 @@ import java.nio.file.Files;
import java.util.Collections;
import java.util.List;
-import coil.Coil;
-import coil.disk.DiskCache;
-import coil.memory.MemoryCache;
+import coil3.SingletonImageLoader;
+import coil3.disk.DiskCache;
+import coil3.memory.MemoryCache;
public final class ShareUtils {
private static final String TAG = ShareUtils.class.getSimpleName();
@@ -377,13 +378,13 @@ public final class ShareUtils {
// Save the image in memory to the application's cache because we need a URI to the
// image to generate a ClipData which will show the share sheet, and so an image file
final Context applicationContext = context.getApplicationContext();
- final var loader = Coil.imageLoader(context);
+ final var loader = SingletonImageLoader.get(context);
final var value = loader.getMemoryCache()
.get(new MemoryCache.Key(thumbnailUrl, Collections.emptyMap()));
final Bitmap cachedBitmap;
if (value != null) {
- cachedBitmap = value.getBitmap();
+ cachedBitmap = toBitmap(value.getImage());
} else {
try (var snapshot = loader.getDiskCache().openSnapshot(thumbnailUrl)) {
if (snapshot != null) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt
index 2608090dc..5b393658c 100644
--- a/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/util/image/CoilHelper.kt
@@ -5,14 +5,18 @@ import android.graphics.Bitmap
import android.util.Log
import android.widget.ImageView
import androidx.annotation.DrawableRes
-import androidx.core.graphics.drawable.toBitmapOrNull
-import coil.executeBlocking
-import coil.imageLoader
-import coil.request.Disposable
-import coil.request.ImageRequest
-import coil.size.Size
-import coil.target.Target
-import coil.transform.Transformation
+import coil3.executeBlocking
+import coil3.imageLoader
+import coil3.request.Disposable
+import coil3.request.ImageRequest
+import coil3.request.error
+import coil3.request.placeholder
+import coil3.request.target
+import coil3.request.transformations
+import coil3.size.Size
+import coil3.target.Target
+import coil3.toBitmap
+import coil3.transform.Transformation
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Image
@@ -26,84 +30,119 @@ object CoilHelper {
fun loadBitmapBlocking(
context: Context,
url: String?,
- @DrawableRes placeholderResId: Int = 0
- ): Bitmap? {
- val request = getImageRequest(context, url, placeholderResId).build()
- return context.imageLoader.executeBlocking(request).drawable?.toBitmapOrNull()
- }
+ @DrawableRes placeholderResId: Int = 0,
+ ): Bitmap? =
+ context.imageLoader
+ .executeBlocking(getImageRequest(context, url, placeholderResId).build())
+ .image
+ ?.toBitmap()
- fun loadAvatar(target: ImageView, images: List) {
+ fun loadAvatar(
+ target: ImageView,
+ images: List,
+ ) {
loadImageDefault(target, images, R.drawable.placeholder_person)
}
- fun loadAvatar(target: ImageView, url: String?) {
+ fun loadAvatar(
+ target: ImageView,
+ url: String?,
+ ) {
loadImageDefault(target, url, R.drawable.placeholder_person)
}
- fun loadThumbnail(target: ImageView, images: List) {
+ fun loadThumbnail(
+ target: ImageView,
+ images: List,
+ ) {
loadImageDefault(target, images, R.drawable.placeholder_thumbnail_video)
}
- fun loadThumbnail(target: ImageView, url: String?) {
+ fun loadThumbnail(
+ target: ImageView,
+ url: String?,
+ ) {
loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video)
}
- fun loadScaledDownThumbnail(context: Context, images: List, target: Target): Disposable {
+ fun loadScaledDownThumbnail(
+ context: Context,
+ images: List,
+ target: Target,
+ ): Disposable {
val url = ImageStrategy.choosePreferredImage(images)
- val request = getImageRequest(context, url, R.drawable.placeholder_thumbnail_video)
- .target(target)
- .transformations(object : Transformation {
- override val cacheKey = "COIL_PLAYER_THUMBNAIL_TRANSFORMATION_KEY"
+ val request =
+ getImageRequest(context, url, R.drawable.placeholder_thumbnail_video)
+ .target(target)
+ .transformations(
+ object : Transformation() {
+ override val cacheKey = "COIL_PLAYER_THUMBNAIL_TRANSFORMATION_KEY"
- override suspend fun transform(input: Bitmap, size: Size): Bitmap {
- if (MainActivity.DEBUG) {
- Log.d(TAG, "Thumbnail - transform() called")
- }
+ override suspend fun transform(
+ input: Bitmap,
+ size: Size,
+ ): Bitmap {
+ if (MainActivity.DEBUG) {
+ Log.d(TAG, "Thumbnail - transform() called")
+ }
- val notificationThumbnailWidth = min(
- context.resources.getDimension(R.dimen.player_notification_thumbnail_width),
- input.width.toFloat()
- ).toInt()
+ val notificationThumbnailWidth =
+ min(
+ context.resources.getDimension(R.dimen.player_notification_thumbnail_width),
+ input.width.toFloat(),
+ ).toInt()
- var newHeight = input.height / (input.width / notificationThumbnailWidth)
- val result = input.scale(notificationThumbnailWidth, newHeight)
+ var newHeight = input.height / (input.width / notificationThumbnailWidth)
+ val result = input.scale(notificationThumbnailWidth, newHeight)
- return if (result == input || !result.isMutable) {
- // create a new mutable bitmap to prevent strange crashes on some
- // devices (see #4638)
- newHeight = input.height / (input.width / (notificationThumbnailWidth - 1))
- input.scale(notificationThumbnailWidth, newHeight)
- } else {
- result
- }
- }
- })
- .build()
+ return if (result == input || !result.isMutable) {
+ // create a new mutable bitmap to prevent strange crashes on some
+ // devices (see #4638)
+ newHeight = input.height / (input.width / (notificationThumbnailWidth - 1))
+ input.scale(notificationThumbnailWidth, newHeight)
+ } else {
+ result
+ }
+ }
+ },
+ ).build()
return context.imageLoader.enqueue(request)
}
- fun loadDetailsThumbnail(target: ImageView, images: List) {
+ fun loadDetailsThumbnail(
+ target: ImageView,
+ images: List,
+ ) {
val url = ImageStrategy.choosePreferredImage(images)
loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video, false)
}
- fun loadBanner(target: ImageView, images: List