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 @@


-

ScreenshotsDescriçãoCaracterísticasAtualizaçõesContribuiçãoDoarLicença

+

ScreenshotsServiços SuportadosDescriçãoRecursosInstalação e atualizaçõesContribuiçõesDoarLicença

SiteBlogFAQPress


-*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). -Translation status +Estado da tradução ## 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). - - + +
LiberapayVisit NewPipe at liberapay.comDonate via LiberapayVisite NewPipe em liberapay.comDoar via Liberapay
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 @@ - - - - - - - - - - - - - - - - - -