diff --git a/README.md b/README.md index 43cbd87..61e7b40 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The other app is **[TubeLab](#TubeLab)** a Peertube Android app working for all ## TubeLab -Tubelab is an Android app for Peertube (GNU GPLv3). +Tubelab is an Android app for Peertube (GNU GPLv3). [Get it on Google Play](https://play.google.com/store/apps/details?id=app.fedilab.tubelab)   [Get it on F-Droid](https://f-droid.org/packages/app.fedilab.tubelab/) diff --git a/app/build.gradle b/app/build.gradle index d3ab720..41714f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,18 +1,16 @@ apply plugin: 'com.android.application' - apply plugin: "androidx.navigation.safeargs" android { compileSdkVersion 30 buildToolsVersion "30.0.2" - defaultConfig { minSdkVersion 21 targetSdkVersion 30 - versionCode 25 - versionName "1.7.0" + versionCode 31 + versionName "1.10.1" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -43,27 +41,70 @@ android { productFlavors { fdroid_acad { applicationId "app.fedilab.fedilabtube" + resValue "string", "app_name", "TubeAcad" + resValue "string", "app_id", "app.fedilab.fedilabtube" buildConfigField "String", "version", "\"fdroid_acad\"" buildConfigField "boolean", "full_instances", "false" buildConfigField "boolean", "google_restriction", "false" + buildConfigField "boolean", "surfing_mode", "false" + buildConfigField "boolean", "sepia_search", "false" + buildConfigField "boolean", "instance_switcher", "true" } google_acad { applicationId "app.fedilab.fedilabtube" + resValue "string", "app_name", "TubeAcad" + resValue "string", "app_id", "app.fedilab.fedilabtube" buildConfigField "String", "version", "\"google_acad\"" buildConfigField "boolean", "full_instances", "false" buildConfigField "boolean", "google_restriction", "true" + buildConfigField "boolean", "surfing_mode", "false" + buildConfigField "boolean", "sepia_search", "false" + buildConfigField "boolean", "instance_switcher", "true" } fdroid_full { applicationId "app.fedilab.tubelab" + resValue "string", "app_name", "TubeLab" + resValue "string", "app_id", "app.fedilab.tubelab" buildConfigField "String", "version", "\"fdroid_full\"" buildConfigField "boolean", "full_instances", "true" buildConfigField "boolean", "google_restriction", "false" + buildConfigField "boolean", "surfing_mode", "true" + buildConfigField "boolean", "sepia_search", "true" + buildConfigField "boolean", "instance_switcher", "true" } google_full { applicationId "app.fedilab.tubelab" + resValue "string", "app_name", "TubeLab" + resValue "string", "app_id", "app.fedilab.tubelab" buildConfigField "String", "version", "\"google_full\"" buildConfigField "boolean", "full_instances", "true" buildConfigField "boolean", "google_restriction", "true" + buildConfigField "boolean", "surfing_mode", "true" + buildConfigField "boolean", "sepia_search", "true" + buildConfigField "boolean", "instance_switcher", "true" + } + queermotion { + applicationId "org.queermotion.peertube" + resValue "string", "app_name", "QueerMotion" + resValue "string", "app_id", "org.queermotion.peertube" + buildConfigField "String", "version", "\"queermotion\"" + buildConfigField "boolean", "full_instances", "true" + buildConfigField "boolean", "google_restriction", "false" + buildConfigField "boolean", "surfing_mode", "false" + buildConfigField "boolean", "sepia_search", "false" + buildConfigField "boolean", "instance_switcher", "false" + } + bittube { + applicationId "app.fedilab.bittube" + resValue "string", "app_name", "Bittube" + resValue "string", "app_id", "app.fedilab.bittube" + buildConfigField "String", "version", "\"bittube\"" + buildConfigField "boolean", "full_instances", "true" + buildConfigField "boolean", "google_restriction", "true" + buildConfigField "boolean", "surfing_mode", "false" + buildConfigField "boolean", "sepia_search", "false" + buildConfigField "boolean", "instance_switcher", "true" + } } @@ -80,6 +121,12 @@ android { google_full { res.srcDirs = ['src/main/res', 'src/full/res'] } + queermotion { + res.srcDirs = ['src/main/res', 'src/queermotion/res'] + } + bittube { + res.srcDirs = ['src/main/res', 'src/bittube/res'] + } } } @@ -99,14 +146,14 @@ dependencies { implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.vectordrawable:vectordrawable:1.1.0' - implementation 'androidx.navigation:navigation-fragment:2.3.1' + implementation 'androidx.navigation:navigation-fragment:2.3.2' implementation "androidx.fragment:fragment:1.2.5" - implementation 'androidx.navigation:navigation-ui:2.3.1' - implementation ("androidx.navigation:navigation-dynamic-features-fragment:2.3.1") + implementation 'androidx.navigation:navigation-ui:2.3.2' + implementation ("androidx.navigation:navigation-dynamic-features-fragment:2.3.2") implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.browser:browser:1.2.0' + implementation 'androidx.browser:browser:1.3.0' implementation 'androidx.documentfile:documentfile:1.0.1' - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.1' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' @@ -118,10 +165,10 @@ dependencies { implementation "com.github.bumptech.glide:glide:4.11.0" annotationProcessor "com.github.bumptech.glide:compiler:4.11.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "net.gotev:uploadservice:3.5.2" - implementation "net.gotev:uploadservice-okhttp:3.5.2" + implementation "net.gotev:uploadservice:4.5.1" + implementation "net.gotev:uploadservice-okhttp:4.5.1" implementation "com.google.code.gson:gson:2.8.6" - implementation 'androidx.media:media:1.2.0' + implementation 'androidx.media:media:1.2.1' implementation 'com.github.ybq:Android-SpinKit:1.4.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' @@ -134,6 +181,12 @@ dependencies { implementation "androidx.work:work-runtime:2.4.0" implementation "androidx.work:work-runtime-ktx:2.4.0" + + //custom cast feature implementation 'jp.wasabeef:glide-transformations:4.0.0' + implementation 'su.litvak.chromecast:api-v2:0.11.3' + implementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' + implementation 'org.slf4j:slf4j-simple:1.7.30' + } \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi-v24/ic_notification_tubelab.xml b/app/src/acad/res/drawable-anydpi-v24/ic_notification_tubelab.xml similarity index 100% rename from app/src/main/res/drawable-anydpi-v24/ic_notification_tubelab.xml rename to app/src/acad/res/drawable-anydpi-v24/ic_notification_tubelab.xml diff --git a/app/src/main/res/drawable-hdpi/ic_notification_tubelab.png b/app/src/acad/res/drawable-hdpi/ic_notification_tubelab.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_notification_tubelab.png rename to app/src/acad/res/drawable-hdpi/ic_notification_tubelab.png diff --git a/app/src/main/res/drawable-mdpi/ic_notification_tubelab.png b/app/src/acad/res/drawable-mdpi/ic_notification_tubelab.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_notification_tubelab.png rename to app/src/acad/res/drawable-mdpi/ic_notification_tubelab.png diff --git a/app/src/main/res/drawable-xhdpi/ic_notification_tubelab.png b/app/src/acad/res/drawable-xhdpi/ic_notification_tubelab.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_notification_tubelab.png rename to app/src/acad/res/drawable-xhdpi/ic_notification_tubelab.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification_tubelab.png b/app/src/acad/res/drawable-xxhdpi/ic_notification_tubelab.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_notification_tubelab.png rename to app/src/acad/res/drawable-xxhdpi/ic_notification_tubelab.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_notification_tubelab.png b/app/src/acad/res/drawable-xxxhdpi/ic_notification_tubelab.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_notification_tubelab.png rename to app/src/acad/res/drawable-xxxhdpi/ic_notification_tubelab.png diff --git a/app/src/acad/res/values/colors.xml b/app/src/acad/res/values/colors.xml index 9fd2099..41d179b 100644 --- a/app/src/acad/res/values/colors.xml +++ b/app/src/acad/res/values/colors.xml @@ -8,6 +8,7 @@ #FAFAFA #2b90d9 #F44336 - + #DD000000 #F44336 + #80808080 \ No newline at end of file diff --git a/app/src/bittube/ic_launcher-playstore.png b/app/src/bittube/ic_launcher-playstore.png new file mode 100644 index 0000000..96e3d80 Binary files /dev/null and b/app/src/bittube/ic_launcher-playstore.png differ diff --git a/app/src/bittube/res/color/bottom_nav_color.xml b/app/src/bittube/res/color/bottom_nav_color.xml new file mode 100644 index 0000000..662d3b2 --- /dev/null +++ b/app/src/bittube/res/color/bottom_nav_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/bittube/res/drawable-anydpi-v24/ic_notification_tubelab.xml b/app/src/bittube/res/drawable-anydpi-v24/ic_notification_tubelab.xml new file mode 100644 index 0000000..26878af --- /dev/null +++ b/app/src/bittube/res/drawable-anydpi-v24/ic_notification_tubelab.xml @@ -0,0 +1,864 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/bittube/res/drawable-hdpi/ic_notification_tubelab.png b/app/src/bittube/res/drawable-hdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..064c676 Binary files /dev/null and b/app/src/bittube/res/drawable-hdpi/ic_notification_tubelab.png differ diff --git a/app/src/bittube/res/drawable-mdpi/ic_notification_tubelab.png b/app/src/bittube/res/drawable-mdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..cd4f8bb Binary files /dev/null and b/app/src/bittube/res/drawable-mdpi/ic_notification_tubelab.png differ diff --git a/app/src/bittube/res/drawable-v24/ic_launcher_foreground.xml b/app/src/bittube/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..3494e3e --- /dev/null +++ b/app/src/bittube/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,862 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/bittube/res/drawable-xhdpi/ic_notification_tubelab.png b/app/src/bittube/res/drawable-xhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..4d6d6d2 Binary files /dev/null and b/app/src/bittube/res/drawable-xhdpi/ic_notification_tubelab.png differ diff --git a/app/src/bittube/res/drawable-xxhdpi/ic_notification_tubelab.png b/app/src/bittube/res/drawable-xxhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..eaa5022 Binary files /dev/null and b/app/src/bittube/res/drawable-xxhdpi/ic_notification_tubelab.png differ diff --git a/app/src/bittube/res/drawable-xxxhdpi/ic_notification_tubelab.png b/app/src/bittube/res/drawable-xxxhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..169866f Binary files /dev/null and b/app/src/bittube/res/drawable-xxxhdpi/ic_notification_tubelab.png differ diff --git a/app/src/bittube/res/drawable/bittube.xml b/app/src/bittube/res/drawable/bittube.xml new file mode 100644 index 0000000..7bca606 --- /dev/null +++ b/app/src/bittube/res/drawable/bittube.xml @@ -0,0 +1,856 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..ac94b34 --- /dev/null +++ b/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..ac94b34 --- /dev/null +++ b/app/src/bittube/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/bittube/res/mipmap-hdpi/ic_launcher.png b/app/src/bittube/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..440ad47 Binary files /dev/null and b/app/src/bittube/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/bittube/res/mipmap-hdpi/ic_launcher_round.png b/app/src/bittube/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..98441c9 Binary files /dev/null and b/app/src/bittube/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/bittube/res/mipmap-mdpi/ic_launcher.png b/app/src/bittube/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..118b9e8 Binary files /dev/null and b/app/src/bittube/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/bittube/res/mipmap-mdpi/ic_launcher_round.png b/app/src/bittube/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..d0754f8 Binary files /dev/null and b/app/src/bittube/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/bittube/res/mipmap-xhdpi/ic_launcher.png b/app/src/bittube/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..5621131 Binary files /dev/null and b/app/src/bittube/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/bittube/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/bittube/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4859fc7 Binary files /dev/null and b/app/src/bittube/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/bittube/res/mipmap-xxhdpi/ic_launcher.png b/app/src/bittube/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..55890cd Binary files /dev/null and b/app/src/bittube/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/bittube/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/bittube/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..99059ce Binary files /dev/null and b/app/src/bittube/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..75a224b Binary files /dev/null and b/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..9b92291 Binary files /dev/null and b/app/src/bittube/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/bittube/res/values/colors.xml b/app/src/bittube/res/values/colors.xml new file mode 100644 index 0000000..e40a9ba --- /dev/null +++ b/app/src/bittube/res/values/colors.xml @@ -0,0 +1,14 @@ + + + #343434 + #343434 + #00abff + + #bbF2690D + #FAFAFA + #2b90d9 + #F44336 + #DD000000 + #F44336 + #80808080 + \ No newline at end of file diff --git a/app/src/bittube/res/values/ic_launcher_background.xml b/app/src/bittube/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/app/src/bittube/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/app/src/bittube/res/xml/file_paths.xml b/app/src/bittube/res/xml/file_paths.xml new file mode 100644 index 0000000..63687c5 --- /dev/null +++ b/app/src/bittube/res/xml/file_paths.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/fdroid_acad/contact-website.txt b/app/src/fdroid_acad/contact-website.txt deleted file mode 100644 index ff945d0..0000000 --- a/app/src/fdroid_acad/contact-website.txt +++ /dev/null @@ -1 +0,0 @@ -https://fedilab.app \ No newline at end of file diff --git a/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/30.txt b/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/30.txt new file mode 100644 index 0000000..78ac812 --- /dev/null +++ b/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/30.txt @@ -0,0 +1,11 @@ +Added: +- Chromecast support (default disabled) +- Detect start time in URLs + +Fixed: +- Typo +- Comment feature when logged out +- Full-screen breaks +- Crashes with the download button on live streams +- Jumps with full-screen and vertical videos +- Abuse report notifications clickable \ No newline at end of file diff --git a/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/31.txt b/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/31.txt new file mode 100644 index 0000000..3a1639e --- /dev/null +++ b/app/src/fdroid_acad/fastlane/metadata/android/en-US/changelogs/31.txt @@ -0,0 +1,13 @@ +Fixes some issues on 1.10.0 (Crashes when adding a playlist + bad behavior when subscribing to remote accounts) + +Added: +- Chromecast support (default disabled) +- Detect start time in URLs + +Fixed: +- Typo +- Comment feature when logged out +- Full-screen breaks +- Crashes with the download button on live streams +- Jumps with full-screen and vertical videos +- Abuse report notifications clickable \ No newline at end of file diff --git a/app/src/fdroid_acad/play/listings/en-US/full-description.txt b/app/src/fdroid_acad/fastlane/metadata/android/en-US/full_description.txt similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/full-description.txt rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/full_description.txt diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/icon/icon.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/icon.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/icon/icon.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/icon.png diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img1.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img1.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img1.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img1.png diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img2.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img2.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img2.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img2.png diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img3.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img3.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img3.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img3.png diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img4.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img4.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img4.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img4.png diff --git a/app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img5.png b/app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img5.png similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/graphics/phone-screenshots/img5.png rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/images/phoneScreenshots/img5.png diff --git a/app/src/fdroid_acad/fastlane/metadata/android/en-US/short_description.txt b/app/src/fdroid_acad/fastlane/metadata/android/en-US/short_description.txt new file mode 100644 index 0000000..6592b98 --- /dev/null +++ b/app/src/fdroid_acad/fastlane/metadata/android/en-US/short_description.txt @@ -0,0 +1 @@ +TubeAcad est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_acad/play/listings/en-US/title.txt b/app/src/fdroid_acad/fastlane/metadata/android/en-US/title.txt similarity index 100% rename from app/src/fdroid_acad/play/listings/en-US/title.txt rename to app/src/fdroid_acad/fastlane/metadata/android/en-US/title.txt diff --git a/app/src/fdroid_acad/play/listings/en-US/short-description.txt b/app/src/fdroid_acad/play/listings/en-US/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_acad/play/listings/en-US/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_acad/play/release-notes/en-US/default.txt b/app/src/fdroid_acad/play/release-notes/en-US/default.txt deleted file mode 100644 index ed45711..0000000 --- a/app/src/fdroid_acad/play/release-notes/en-US/default.txt +++ /dev/null @@ -1,5 +0,0 @@ -- Nouvelles options : - - Mode plein écran automatique - - Désactiver la lecture automatique des vidéos - -- Quelques corrections de bugs \ No newline at end of file diff --git a/app/src/fdroid_full/contact-website.txt b/app/src/fdroid_full/contact-website.txt deleted file mode 100644 index ff945d0..0000000 --- a/app/src/fdroid_full/contact-website.txt +++ /dev/null @@ -1 +0,0 @@ -https://fedilab.app \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/af/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ar/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/af/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ar/full_description.txt diff --git a/app/src/fdroid_full/play/listings/ar/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ar/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ar/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ar/short_description.txt diff --git a/app/src/fdroid_full/play/listings/de/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/de/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/de/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/de/full_description.txt diff --git a/app/src/fdroid_full/play/listings/de/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/de/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/de/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/de/short_description.txt diff --git a/app/src/fdroid_full/play/listings/el/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/el/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/el/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/el/full_description.txt diff --git a/app/src/fdroid_full/play/listings/el/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/el/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/el/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/el/short_description.txt diff --git a/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/30.txt b/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/30.txt new file mode 100644 index 0000000..5feb62c --- /dev/null +++ b/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/30.txt @@ -0,0 +1,15 @@ +Added: +- Chromecast support (default disabled) +- Detect start time in URLs + +Changed: +- Instance picker supports URLs + +Fixed: +- Typo +- Comment feature when logged out +- Full-screen breaks +- Crashes with the download button on live streams +- Jumps with full-screen and vertical videos +- Abuse report notifications clickable +- Remote channel subscriptions need twice clicks \ No newline at end of file diff --git a/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/31.txt b/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/31.txt new file mode 100644 index 0000000..0e49659 --- /dev/null +++ b/app/src/fdroid_full/fastlane/metadata/android/en-US/changelogs/31.txt @@ -0,0 +1,16 @@ +Fixes some issues on 1.10.0 (Crashes when adding a playlist + bad behavior when subscribing to remote accounts) +Added: +- Chromecast support (default disabled) +- Detect start time in URLs + +Changed: +- Instance picker supports URLs + +Fixed: +- Typo +- Comment feature when logged out +- Full-screen breaks +- Crashes with the download button on live streams +- Jumps with full-screen and vertical videos +- Abuse report notifications clickable +- Remote channel subscriptions need twice clicks \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/ar/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/en-US/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ar/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/en-US/full_description.txt diff --git a/app/src/fdroid_full/play/listings/en-US/graphics/icon/icon.png b/app/src/fdroid_full/fastlane/metadata/android/en-US/images/icon.png similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/graphics/icon/icon.png rename to app/src/fdroid_full/fastlane/metadata/android/en-US/images/icon.png diff --git a/app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img1.png b/app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img1.png similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img1.png rename to app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img1.png diff --git a/app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img2.png b/app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img2.png similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img2.png rename to app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img2.png diff --git a/app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img3.png b/app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img3.png similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img3.png rename to app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img3.png diff --git a/app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img4.png b/app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img4.png similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/graphics/phone-screenshots/img4.png rename to app/src/fdroid_full/fastlane/metadata/android/en-US/images/phoneScreenshots/img4.png diff --git a/app/src/fdroid_full/play/listings/en-US/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/en-US/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/en-US/short_description.txt diff --git a/app/src/fdroid_full/play/listings/en-US/title.txt b/app/src/fdroid_full/fastlane/metadata/android/en-US/title.txt similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/title.txt rename to app/src/fdroid_full/fastlane/metadata/android/en-US/title.txt diff --git a/app/src/fdroid_full/play/listings/es/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/es/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/es/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/es/full_description.txt diff --git a/app/src/fdroid_full/play/listings/es/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/es/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/es/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/es/short_description.txt diff --git a/app/src/fdroid_full/play/listings/fr/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/fr/full_description.txt similarity index 87% rename from app/src/fdroid_full/play/listings/fr/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/fr/full_description.txt index 5c74acd..efb3dbe 100644 --- a/app/src/fdroid_full/play/listings/fr/full-description.txt +++ b/app/src/fdroid_full/fastlane/metadata/android/fr/full_description.txt @@ -12,7 +12,7 @@ C'est un mode limité où vous pouvez faire certaines actions: De nombreuses fonctionnalités sont disponibles avec ce mode: - Rédiger/supprimer des commentaires -- Télécharger/supprimer/modifier des vidéos +- Téléverser/supprimer/modifier des vidéos - Gérer (créer/modifier/supprimer) les chaînes et les listes de lecture - Suivre/ne pas suivre les canaux - Pouces vers le haut/vers le bas diff --git a/app/src/fdroid_full/play/listings/fr/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/fr/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/fr/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/fr/short_description.txt diff --git a/app/src/fdroid_full/play/listings/it/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/it/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/it/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/it/full_description.txt diff --git a/app/src/fdroid_full/play/listings/it/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/it/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/it/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/it/short_description.txt diff --git a/app/src/fdroid_full/play/listings/ca/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ja/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ca/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ja/full_description.txt diff --git a/app/src/fdroid_full/play/listings/ja/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ja/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ja/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ja/short_description.txt diff --git a/app/src/fdroid_full/play/listings/cs/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ko/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/cs/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ko/full_description.txt diff --git a/app/src/fdroid_full/play/listings/ko/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ko/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ko/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ko/short_description.txt diff --git a/app/src/fdroid_full/play/listings/nl/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/nl/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/nl/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/nl/full_description.txt diff --git a/app/src/fdroid_full/play/listings/nl/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/nl/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/nl/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/nl/short_description.txt diff --git a/app/src/fdroid_full/play/listings/pl/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/pl/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/pl/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/pl/full_description.txt diff --git a/app/src/fdroid_full/play/listings/pl/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/pl/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/pl/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/pl/short_description.txt diff --git a/app/src/fdroid_full/play/listings/pt/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/pt/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/pt/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/pt/full_description.txt diff --git a/app/src/fdroid_full/play/listings/pt/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/pt/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/pt/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/pt/short_description.txt diff --git a/app/src/fdroid_full/play/listings/da/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ro/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/da/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ro/full_description.txt diff --git a/app/src/fdroid_full/play/listings/ro/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ro/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ro/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ro/short_description.txt diff --git a/app/src/fdroid_full/play/listings/ru/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ru/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ru/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ru/full_description.txt diff --git a/app/src/fdroid_full/play/listings/ru/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/ru/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/ru/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/ru/short_description.txt diff --git a/app/src/fdroid_full/play/listings/en-US/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/sv/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/en-US/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/sv/full_description.txt diff --git a/app/src/fdroid_full/play/listings/sv/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/sv/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/sv/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/sv/short_description.txt diff --git a/app/src/fdroid_full/play/listings/zh-rCN/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/zh-rCN/full_description.txt similarity index 81% rename from app/src/fdroid_full/play/listings/zh-rCN/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/zh-rCN/full_description.txt index 96d1795..38553d9 100644 --- a/app/src/fdroid_full/play/listings/zh-rCN/full-description.txt +++ b/app/src/fdroid_full/fastlane/metadata/android/zh-rCN/full_description.txt @@ -1,6 +1,6 @@ *游客模式* -此种受限模式下,您只能进行进行部分操作: +在该模式下, 功能受到限制 • 切换实例 • 分享视频 diff --git a/app/src/fdroid_full/play/listings/zh-rCN/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/zh-rCN/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/zh-rCN/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/zh-rCN/short_description.txt diff --git a/app/src/fdroid_full/play/listings/en/full-description.txt b/app/src/fdroid_full/fastlane/metadata/android/zh-rTW/full_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/en/full-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/zh-rTW/full_description.txt diff --git a/app/src/fdroid_full/play/listings/zh-rTW/short-description.txt b/app/src/fdroid_full/fastlane/metadata/android/zh-rTW/short_description.txt similarity index 100% rename from app/src/fdroid_full/play/listings/zh-rTW/short-description.txt rename to app/src/fdroid_full/fastlane/metadata/android/zh-rTW/short_description.txt diff --git a/app/src/fdroid_full/play/listings/af/short-description.txt b/app/src/fdroid_full/play/listings/af/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/af/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/ca/short-description.txt b/app/src/fdroid_full/play/listings/ca/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/ca/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/cs/short-description.txt b/app/src/fdroid_full/play/listings/cs/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/cs/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/da/short-description.txt b/app/src/fdroid_full/play/listings/da/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/da/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/en/short-description.txt b/app/src/fdroid_full/play/listings/en/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/en/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/fi/full-description.txt b/app/src/fdroid_full/play/listings/fi/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/fi/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/fi/short-description.txt b/app/src/fdroid_full/play/listings/fi/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/fi/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/he/full-description.txt b/app/src/fdroid_full/play/listings/he/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/he/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/he/short-description.txt b/app/src/fdroid_full/play/listings/he/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/he/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/hu/full-description.txt b/app/src/fdroid_full/play/listings/hu/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/hu/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/hu/short-description.txt b/app/src/fdroid_full/play/listings/hu/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/hu/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/ja/full-description.txt b/app/src/fdroid_full/play/listings/ja/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/ja/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/ko/full-description.txt b/app/src/fdroid_full/play/listings/ko/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/ko/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/no/full-description.txt b/app/src/fdroid_full/play/listings/no/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/no/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/no/short-description.txt b/app/src/fdroid_full/play/listings/no/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/no/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/ro/full-description.txt b/app/src/fdroid_full/play/listings/ro/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/ro/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/sr/full-description.txt b/app/src/fdroid_full/play/listings/sr/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/sr/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/sr/short-description.txt b/app/src/fdroid_full/play/listings/sr/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/sr/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/sv/full-description.txt b/app/src/fdroid_full/play/listings/sv/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/sv/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/tr/full-description.txt b/app/src/fdroid_full/play/listings/tr/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/tr/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/tr/short-description.txt b/app/src/fdroid_full/play/listings/tr/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/tr/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/uk/full-description.txt b/app/src/fdroid_full/play/listings/uk/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/uk/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/uk/short-description.txt b/app/src/fdroid_full/play/listings/uk/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/uk/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/vi/full-description.txt b/app/src/fdroid_full/play/listings/vi/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/vi/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/vi/short-description.txt b/app/src/fdroid_full/play/listings/vi/short-description.txt deleted file mode 100644 index 1f0744b..0000000 --- a/app/src/fdroid_full/play/listings/vi/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -TubeLab est une application Peertube pour les instances académiques. \ No newline at end of file diff --git a/app/src/fdroid_full/play/listings/zh-rTW/full-description.txt b/app/src/fdroid_full/play/listings/zh-rTW/full-description.txt deleted file mode 100644 index fd254e1..0000000 --- a/app/src/fdroid_full/play/listings/zh-rTW/full-description.txt +++ /dev/null @@ -1,22 +0,0 @@ -*Not authenticated mode* - -It's a limited mode where you can do some actions: - -- Switch instance, -- Share videos, -- Download videos. - - -*Authenticated mode* - -Many features are available with this mode: - -- Write/delete comments -- Upload/remove/edit videos -- Manage (create/edit/remove) channels and playlists -- Follow/unfollow channels -- Thumbs-up/down -- Check notifications -- Mute/unmute channels -- Report videos/accounts -- Check your history \ No newline at end of file diff --git a/app/src/fdroid_full/play/release-notes/en-US/default.txt b/app/src/fdroid_full/play/release-notes/en-US/default.txt deleted file mode 100644 index b010cef..0000000 --- a/app/src/fdroid_full/play/release-notes/en-US/default.txt +++ /dev/null @@ -1,14 +0,0 @@ -Added: -- Display follow button for Sepia Search -- Reach owner from channels -- Surfing mode (store instances in db with quick switch) -- Allow to delete history - -Changed: -- Quicker access to account settings -- Improve landscape mode - -Fix: -- Pull to refresh crashes -- Settings crashes -- Fix an issue with unlisted playlists \ No newline at end of file diff --git a/app/src/full/res/drawable-anydpi-v24/ic_notification_tubelab.xml b/app/src/full/res/drawable-anydpi-v24/ic_notification_tubelab.xml new file mode 100644 index 0000000..4caaeda --- /dev/null +++ b/app/src/full/res/drawable-anydpi-v24/ic_notification_tubelab.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/full/res/drawable-hdpi/ic_notification_tubelab.png b/app/src/full/res/drawable-hdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..27f4dd8 Binary files /dev/null and b/app/src/full/res/drawable-hdpi/ic_notification_tubelab.png differ diff --git a/app/src/full/res/drawable-mdpi/ic_notification_tubelab.png b/app/src/full/res/drawable-mdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..d7cd8a2 Binary files /dev/null and b/app/src/full/res/drawable-mdpi/ic_notification_tubelab.png differ diff --git a/app/src/full/res/drawable-xhdpi/ic_notification_tubelab.png b/app/src/full/res/drawable-xhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..8633430 Binary files /dev/null and b/app/src/full/res/drawable-xhdpi/ic_notification_tubelab.png differ diff --git a/app/src/full/res/drawable-xxhdpi/ic_notification_tubelab.png b/app/src/full/res/drawable-xxhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..e9879d9 Binary files /dev/null and b/app/src/full/res/drawable-xxhdpi/ic_notification_tubelab.png differ diff --git a/app/src/full/res/drawable-xxxhdpi/ic_notification_tubelab.png b/app/src/full/res/drawable-xxxhdpi/ic_notification_tubelab.png new file mode 100644 index 0000000..21a32b0 Binary files /dev/null and b/app/src/full/res/drawable-xxxhdpi/ic_notification_tubelab.png differ diff --git a/app/src/full/res/values-fr/strings.xml b/app/src/full/res/values-fr/strings.xml deleted file mode 100644 index 008b24a..0000000 --- a/app/src/full/res/values-fr/strings.xml +++ /dev/null @@ -1,326 +0,0 @@ - - - Videos in list - Change the layout for displaying videos in a list - No instances ! - Show more - Show less - Screen lock - Keep playing videos when the screen is locked - Save - Enable history - Change profile picture - Automatic playback - If enabled, videos will be played automatically - Fullscreen - Automatically open videos in fullscreen - Automatically start playing the next video - When a video ends, follow up with the next suggested video. - Ajouter une réponse publique - Activity - App - New video from your subscriptions - New comment on your video - One of your video is blocked/unblocked - Video published (after transcoding/scheduled update) - Video import finished - You or your channel(s) has a new follower - Someone mentioned you in video comments - An abuse report received a new message - One of your abuse reports has been accepted or rejected by moderators - - %d réponse - %d réponses - - Réponse - Thème - Permet de changer le thème de l\'application - La vidéo ne peut pas être fédérée ! - Locale - Locale - Découvrir - Notifications - Nouveautés - Tendances - Plus aimées - Une erreur s\'est produite ! - Sourdine - Chaînes - Ne pas lister - Estomper - Afficher - Pas d\'opinion - Choisissez une instance - Cette instance ne semble pas être valide ! - Aucune vidéo ! - Aucune notification ! - Favicon - Ouvrir avec - Modifier une liste de lecture - Fermer - Téléverser - Aperçu de l\'image - Sélectionnez un fichier à transférer - New video - New blacklist info - Your video is published - Error when publishing your video - New comment - New follow - Chaîne - Vidéos - Chaînes - Fetch every: - - Never - 15 minutes - 30 minutes - 1 hour - 2 hours - 6 hours - 12 hours - - Oui - Non - Annuler - Télécharger - Photo du profil - Mettre à jour la vidéo - Supprimer de la liste de lecture - %d s - %d min - %d h - %d j - %s vues - Domaine de l\'instance - Transfert en cours, veuillez patienter … - La vidéo a été transférée ! - Transfert annulé ! - Cliquez ici pour éditer les données de la vidéo. - Une erreur s’est produite lors de la sélection du média ! - Télécharger %1$s - The account has been updated! - Confidentialité - Déconnexion - Connexion - Mot de passe - Courriel - Étiquettes - Valider - Partager avec - Partagé via TubeLab - Nom d’utilisateur - Paramètres - Voulez-vous vraiment déconnecter le compte @%1$s@%2$s ? - Suit - Abonné·e·s - Impossible d’obtenir l’id du client ! - Une erreur s’est produite pendant le chargement du compte ! - Une erreur s’est produite lors de la recherche ! - Aucune action ne peut être réalisée - S\'abonner - Mettre en sourdine - Chercher - Supprimer - Êtes-vous sûr de vouloir supprimer définitivement cette liste de lecture ? - Supprimer la liste de lecture - Soyez le·a premier·ère à laisser un commentaire sur cette vidéo en utilisant le bouton supérieur droit ! - Les commentaires sur cette vidéos ont été désactivés ! - Choisissez une résolution - La vidéo est rajoutée aux favoris ! - La vidéo a été retirée de vos favoris ! - Information - Logo de l’application - - Abonnements - Delete an instance - Are you sure to delete this instance? - Supprimer le commentaire - Etes-vous sûr de vouloir supprimer ce commentaire ? - Mode pour les vidéos - Filtrer - Recherche sépia - Afficher le contenu sensible - Date de publication - Toutes - Aujourd\'hui - Les 7 derniers jours - Les 30 derniers jours - Les 365 derniers jours - Durée - - - 10 min)]]> - Afficher toutes les catégories - Afficher toutes les licences - Afficher toutes les langues - Tous ces labels - Un de ces labels - Appliquer le filtre - - Meilleurs résultats - Les plus récentes - Les moins récentes - - Trier par - Mot-clé, chaîne, vidéo, etc. - La recherche Sepia affiche les vidéos et les chaînes qui correspondent à votre recherche mais qui n\'est pas l\'éditeur, ni le propriétaire. Si vous remarquez des problèmes avec une vidéo, signalez-la aux administrateurs sur le site Web de PeerTube où la vidéo est publiée. - Mes vidéos - Titre - Licence - Catégorie - Langue - Cette vidéo contient du contenu pour adultes - Activer les commentaires - Libellé - La vidéo a été mise à jour ! - Créer un compte - Adresse mèl - Aperçu - Modifier l\'aperçu - Nom - Afficher plus - Aucune chaîne ! - Quelques explications concernant votre signalement… - Signaler la vidéo - Signaler - Changer d\'instance - Historique - Modifier - Réglages des vidéos - Interface - Cache - Définir le cache pour les vidéos (par défaut 100Mo) - Définir une qualité par défaut pour les vidéos - Résolution pour les vidéos - Cache vidéo : %d Mo - Légendes - Options d\'envoi - Aucune - Permet de changer le mode de lecture pour les vidéos (normal, streaming ou via un navigateur). - Supprimer les commentaires du compte - Êtes-vous sûr de vouloir supprimer tous les commentaires de ce compte ? - Supprimer la vidéo - Êtes-vous sûr de vouloir supprimer cette vidéo ? - Aucune vidéo n’est disponible ! - Partager - %1$s a commenté votre vidéo %2$s]]> - %1$s suit votre chaîne %2$s]]> - %1$s suit votre compte]]> - %1$s a été publiée]]> - %1$s a réussi]]> - %1$s]]> - %1$s a publié une nouvelle vidéo : %2$s]]> - %1$s a été blacklisté]]> - %1$s n’est plus blacklisté]]> - %1$s has been accepted]]> - %1$s]]> - Ajouter un commentaire public - Envoyer un commentaire - Tout - - Delete videos history - Are you sure you want to delete all your videos history? - Export - Import - Successful export! - Tap here to send the export by email - New Playlist - Open the attached file with TubeLab - Listes de lecture - No playlists - Nom d\'affichage - Vous n\'avez aucune liste de lecture. Cliquez sur l\'icône « + » pour en ajouter une - Vous devez fournir un nom d\'affichage ! - Une chaîne est requise lorsque la liste de lecture est publique. - Créer une liste de lecture - Cette liste de lecture est vide. - Confirmer le mot de passe - J\'accepte les %1$s et les %2$s - règles du serveur - conditions de service - S’inscrire - Veuillez remplir tous les champs ! - Les mots de passe ne sont pas identiques ! - L\'e-mail ne semble pas être valide ! - Vous recevrez un e-mail de confirmation - Utilisez au moins 8 caractères - Le mot de passe doit contenir au moins 8 caractères - Le nom d\'utilisateur·rice doit être en minuscule, contenir uniquement des lettres, des chiffres, des points et des caractères de soulignement - Compte créé ! - Votre compte est créé !\n\nVous allez recevoir un email de confirmation à l\'adresse %1$s.\n\nCliquez sur le lien présent dans le mail pour valider votre compte. - Compte - Signaler le compte - - Normal - Streaming - Magnet - Torrent - - - Lumière - Foncé - Automatique - - - Élevée - Moyenne - Faible - - Voulez-vous vous désabonner de ce compte ? - Titre de la vidéo - Rejoignez Peertube - J\'ai au moins 16 ans et je suis d\'accord avec les %1$s de cette instance - Éditer le profil - Faire une action - Se désabonner - Afficher les vidéos sensibles - Vidéo plein écran - Il n’y a aucune vidéo Peertube dans vos favoris ! - Supprimer la chaîne - Êtes-vous sûr de vouloir supprimer définitivement cette chaîne ? - Vidéo dans les listes de lecture - Aucun compte en sourdine ! - Vous devez fournir un nom d\'affichage et un nom pour la chaîne! - Créer une chaîne - Modifier une chaîne - Les adresses mails %1$s ne sont pas autorisées ! - Veuillez préciser les raisons. - Vous devez être connecté.e pour effectuer cette action ! - Le compte a été signalé ! - Le commentaire a été signalé ! - La vidéo a été signalée ! - Le mot de passe doit contenir 6 caractères ! - Le compte a été mis en sourdine ! - Modifier une vidéo - Créer un compte - %1$s Abonné·e·s - Développeur - Version %1$s - À propos de l’application - Faire un don - Code source - Suivi des tickets - Aucune instance ne correspond à ces critères - Sélecteur d\'instances - Choisissez une instance - Vidéos sensibles - Contenu sensible : %1$s - %1$s instances suiveuses - Aide - Sélection des catégories - Sélection des langues - Mise à jour des informations - Fetch notifications - Ajouter un compte - Liste des comptes - Pause - Jouer - Minimiser - Rembobinage rapide - Avance rapide - Minimiser la taille des vidéos - Minimiser la taille des vidéos lorsque l\'application est en arrière-plan (Android N+) - Filtre de langue - Filtrer les vidéos avec différentes langues - diff --git a/app/src/full/res/values/colors.xml b/app/src/full/res/values/colors.xml index 2547404..8d944f0 100644 --- a/app/src/full/res/values/colors.xml +++ b/app/src/full/res/values/colors.xml @@ -7,6 +7,7 @@ #FAFAFA #2b90d9 #F44336 - + #DD000000 #F44336 + #80808080 \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c55b5a0..bbfff94 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -157,13 +157,6 @@ - - - - - { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.me/Mastalab")); startActivity(browserIntent); }); + if (BuildConfig.FLAVOR.equals("queermotion")) { + donatePaypal.setVisibility(View.GONE); + LinearLayout dev_info = findViewById(R.id.dev_info); + dev_info.setVisibility(View.GONE); + } Button donateLiberapay = findViewById(R.id.donate_liberapay); donateLiberapay.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://liberapay.com/tom79")); + Intent browserIntent; + if (BuildConfig.FLAVOR.equals("queermotion")) { + browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://soutenir.queermotion.org")); + } else { + browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://liberapay.com/tom79")); + } + startActivity(browserIntent); }); diff --git a/app/src/main/java/app/fedilab/fedilabtube/AllPlaylistsActivity.java b/app/src/main/java/app/fedilab/fedilabtube/AllPlaylistsActivity.java index 3fb4710..5dd526b 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/AllPlaylistsActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/AllPlaylistsActivity.java @@ -14,7 +14,12 @@ package app.fedilab.fedilabtube; * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see . */ +import android.Manifest; +import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -27,19 +32,19 @@ import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.Spinner; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; import java.util.ArrayList; import java.util.HashMap; @@ -54,52 +59,53 @@ import app.fedilab.fedilabtube.client.data.ChannelData; import app.fedilab.fedilabtube.client.data.PlaylistData.Playlist; import app.fedilab.fedilabtube.client.entities.Item; import app.fedilab.fedilabtube.client.entities.PlaylistParams; +import app.fedilab.fedilabtube.databinding.ActivityAllPlaylistBinding; +import app.fedilab.fedilabtube.databinding.AddPlaylistBinding; import app.fedilab.fedilabtube.drawer.PlaylistAdapter; +import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; import app.fedilab.fedilabtube.viewmodel.PlaylistsVM; import es.dmoral.toasty.Toasty; -import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; +import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistAdapter.AllPlaylistRemoved { PlaylistAdapter playlistAdapter; - private RelativeLayout mainLoader; - private RelativeLayout textviewNoAction; private HashMap privacyToSend; - private Spinner set_upload_channel; - private Spinner set_upload_privacy; private String idChannel; private List playlists; private Playlist playlistToEdit; private List myChannels; private ChannelData.Channel selectedChannel; + private static final int PICK_AVATAR = 467; + private AddPlaylistBinding bindingDialog; + private Uri inputData; + private ActivityAllPlaylistBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_all_playlist); + binding = ActivityAllPlaylistBinding.inflate(getLayoutInflater()); + View viewRoot = binding.getRoot(); + setContentView(viewRoot); if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); setTitle(R.string.playlists); - textviewNoAction = findViewById(R.id.no_action); - mainLoader = findViewById(R.id.loader); - RelativeLayout nextElementLoader = findViewById(R.id.loading_next_items); - mainLoader.setVisibility(View.VISIBLE); - nextElementLoader.setVisibility(View.GONE); + binding.loader.setVisibility(View.VISIBLE); + binding.loadingNextItems.setVisibility(View.GONE); idChannel = null; PlaylistsVM viewModel = new ViewModelProvider(AllPlaylistsActivity.this).get(PlaylistsVM.class); viewModel.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(AllPlaylistsActivity.this, apiResponse -> manageVIewPlaylists(PlaylistsVM.action.GET_PLAYLISTS, apiResponse)); - FloatingActionButton add_new = findViewById(R.id.add_new); - LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies()); if (privaciesInit.size() > 0) { @@ -110,13 +116,12 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA playlists = new ArrayList<>(); - RecyclerView lv_playlist = findViewById(R.id.lv_playlist); playlistAdapter = new PlaylistAdapter(playlists, false); playlistAdapter.allPlaylistRemoved = this; - lv_playlist.setAdapter(playlistAdapter); + binding.lvPlaylist.setAdapter(playlistAdapter); LinearLayoutManager mLayoutManager = new LinearLayoutManager(AllPlaylistsActivity.this); - lv_playlist.setLayoutManager(mLayoutManager); - add_new.setOnClickListener(view -> manageAlert(null)); + binding.lvPlaylist.setLayoutManager(mLayoutManager); + binding.addNew.setOnClickListener(view -> manageAlert(null)); } @Override @@ -135,7 +140,7 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA public void manageVIewPlaylists(PlaylistsVM.action actionType, APIResponse apiResponse) { - mainLoader.setVisibility(View.GONE); + binding.loader.setVisibility(View.GONE); if (apiResponse.getError() != null) { Toasty.error(AllPlaylistsActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); return; @@ -144,9 +149,9 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA if (apiResponse.getPlaylists() != null && apiResponse.getPlaylists().size() > 0) { playlists.addAll(apiResponse.getPlaylists()); playlistAdapter.notifyDataSetChanged(); - textviewNoAction.setVisibility(View.GONE); + binding.noAction.setVisibility(View.GONE); } else { - textviewNoAction.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.VISIBLE); } } } @@ -155,37 +160,53 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA playlistToEdit = playlistParam; AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AllPlaylistsActivity.this); - LayoutInflater inflater1 = getLayoutInflater(); - View dialogView = inflater1.inflate(R.layout.add_playlist, new LinearLayout(AllPlaylistsActivity.this), false); - dialogBuilder.setView(dialogView); - EditText display_name = dialogView.findViewById(R.id.display_name); - EditText description = dialogView.findViewById(R.id.description); - set_upload_channel = dialogView.findViewById(R.id.set_upload_channel); - set_upload_privacy = dialogView.findViewById(R.id.set_upload_privacy); + bindingDialog = AddPlaylistBinding.inflate(LayoutInflater.from(AllPlaylistsActivity.this), null, false); + dialogBuilder.setView(bindingDialog.getRoot()); + + dialogBuilder.setView(bindingDialog.getRoot()); + ChannelsVM viewModelC = new ViewModelProvider(AllPlaylistsActivity.this).get(ChannelsVM.class); viewModelC.get(RetrofitPeertubeAPI.DataType.MY_CHANNELS, null).observe(AllPlaylistsActivity.this, this::manageVIewChannels); - display_name.setFilters(new InputFilter[]{new InputFilter.LengthFilter(120)}); - description.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1000)}); + bindingDialog.displayName.setFilters(new InputFilter[]{new InputFilter.LengthFilter(120)}); + bindingDialog.description.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1000)}); if (playlistToEdit != null) { - display_name.setText(playlistToEdit.getDisplayName()); - description.setText(playlistToEdit.getDescription()); + bindingDialog.displayName.setText(playlistToEdit.getDisplayName()); + bindingDialog.description.setText(playlistToEdit.getDescription()); } dialogBuilder.setPositiveButton(R.string.validate, null); dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); AlertDialog alertDialog = dialogBuilder.create(); + + bindingDialog.selectFile.setOnClickListener(v -> { + if (ContextCompat.checkSelfPermission(AllPlaylistsActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(AllPlaylistsActivity.this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); + return; + } + + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + String[] mimetypes = {"image/*"}; + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes); + startActivityForResult(intent, PICK_AVATAR); + }); + Helper.loadGiF(AllPlaylistsActivity.this, playlistParam != null ? playlistParam.getThumbnailPath() : null, bindingDialog.profilePicture); alertDialog.setOnShowListener(dialogInterface -> { Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); button.setOnClickListener(view -> { - if (display_name.getText() != null && display_name.getText().toString().trim().length() > 0) { + if (bindingDialog.displayName.getText() != null && bindingDialog.displayName.getText().toString().trim().length() > 0) { PlaylistParams playlistElement = new PlaylistParams(); - playlistElement.setDisplayName(display_name.getText().toString().trim()); - if (description.getText() != null && description.getText().toString().trim().length() > 0) { - playlistElement.setDescription(description.getText().toString().trim()); + playlistElement.setDisplayName(bindingDialog.displayName.getText().toString().trim()); + if (bindingDialog.description.getText() != null && bindingDialog.description.getText().toString().trim().length() > 0) { + playlistElement.setDescription(bindingDialog.description.getText().toString().trim()); } playlistElement.setVideoChannelId(idChannel); String label; @@ -203,11 +224,11 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA new Thread(() -> { String playlistId; if (playlistToEdit == null) { - APIResponse apiResponse = new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.CREATE_PLAYLIST, null, playlistElement, null); + APIResponse apiResponse = new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.CREATE_PLAYLIST, null, playlistElement, inputData); playlistId = apiResponse.getActionReturn(); } else { playlistId = playlistToEdit.getId(); - new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.UPDATE_PLAYLIST, playlistId, playlistElement, null); + new RetrofitPeertubeAPI(AllPlaylistsActivity.this).createOrUpdatePlaylist(PlaylistsVM.action.UPDATE_PLAYLIST, playlistId, playlistElement, inputData); } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { @@ -247,7 +268,7 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA //Hide keyboard InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); assert imm != null; - imm.hideSoftInputFromWindow(display_name.getWindowToken(), 0); + imm.hideSoftInputFromWindow(bindingDialog.displayName.getWindowToken(), 0); }); if (alertDialog.getWindow() != null) alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); @@ -255,6 +276,24 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == PICK_AVATAR && resultCode == Activity.RESULT_OK) { + if (data == null || data.getData() == null) { + Toasty.error(AllPlaylistsActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show(); + return; + } + inputData = data.getData(); + Glide.with(AllPlaylistsActivity.this) + .load(inputData) + .thumbnail(0.1f) + .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) + .into(bindingDialog.profilePicture); + + } + } + public void manageVIewChannels(APIResponse apiResponse) { if (apiResponse.getError() != null || apiResponse.getChannels() == null || apiResponse.getChannels().size() == 0) { if (apiResponse.getError() != null && apiResponse.getError().getError() != null) @@ -280,7 +319,7 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA ArrayAdapter adapterChannel = new ArrayAdapter<>(AllPlaylistsActivity.this, android.R.layout.simple_spinner_dropdown_item, channelName); - set_upload_channel.setAdapter(adapterChannel); + bindingDialog.setUploadChannel.setAdapter(adapterChannel); LinkedHashMap translations = null; @@ -308,19 +347,19 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA ArrayAdapter adapterPrivacies = new ArrayAdapter<>(AllPlaylistsActivity.this, android.R.layout.simple_spinner_dropdown_item, privaciesA); - set_upload_privacy.setAdapter(adapterPrivacies); + bindingDialog.setUploadPrivacy.setAdapter(adapterPrivacies); if (playlistToEdit != null) { Item privacy = playlistToEdit.getPrivacy(); if (privacy.getId() > 0) { - set_upload_privacy.setSelection(privacy.getId() - 1); + bindingDialog.setUploadPrivacy.setSelection(privacy.getId() - 1); } } else { - set_upload_privacy.setSelection(2); + bindingDialog.setUploadPrivacy.setSelection(2); } //Manage privacies - set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + bindingDialog.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies()); @@ -348,12 +387,12 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA Item privacy = playlistToEdit.getPrivacy(); if (privacy.getId() > 0) { - set_upload_privacy.setSelection(privacy.getId() - 1); + bindingDialog.setUploadPrivacy.setSelection(privacy.getId() - 1); } } //Manage languages - set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + bindingDialog.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { idChannel = channelId[position]; @@ -378,12 +417,12 @@ public class AllPlaylistsActivity extends AppCompatActivity implements PlaylistA } k++; } - set_upload_channel.setSelection(position); + bindingDialog.setUploadChannel.setSelection(position); } } @Override public void onAllPlaylistRemoved() { - textviewNoAction.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.VISIBLE); } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java b/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java index 95cf7b2..3c12f80 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java +++ b/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java @@ -14,22 +14,30 @@ package app.fedilab.fedilabtube; * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see . */ +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; +import android.os.Build; import androidx.multidex.MultiDex; import androidx.multidex.MultiDexApplication; import androidx.work.Configuration; import androidx.work.WorkManager; -import net.gotev.uploadservice.UploadService; +import net.gotev.uploadservice.UploadServiceConfig; +import net.gotev.uploadservice.observer.request.GlobalRequestObserver; + +import java.util.Objects; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.helper.ThemeHelper; +import app.fedilab.fedilabtube.services.GlobalUploadObserver; import app.fedilab.fedilabtube.worker.WorkHelper; public class FedilabTube extends MultiDexApplication { + static String UPLOAD_CHANNEL_ID = "upload_info_peertube"; @Override public void onCreate() { @@ -44,6 +52,10 @@ public class FedilabTube extends MultiDexApplication { if (interval >= 15) { WorkHelper.fetchNotifications(this, interval); } + createNotificationChannel(); + UploadServiceConfig.initialize(FedilabTube.this, UPLOAD_CHANNEL_ID, true); + + new GlobalRequestObserver(this, new GlobalUploadObserver()); } @@ -52,8 +64,6 @@ public class FedilabTube extends MultiDexApplication { super.attachBaseContext(base); MultiDex.install(FedilabTube.this); - - UploadService.NAMESPACE = BuildConfig.APPLICATION_ID; SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); int themePref = sharedpreferences.getInt(Helper.SET_THEME, Helper.DEFAULT_MODE); ThemeHelper.switchTo(themePref); @@ -61,5 +71,13 @@ public class FedilabTube extends MultiDexApplication { } - + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(UPLOAD_CHANNEL_ID, + getString(R.string.notification_channel_name), + NotificationManager.IMPORTANCE_LOW); + channel.setSound(null, null); + ((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel); + } + } } \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/InstancePickerActivity.java b/app/src/main/java/app/fedilab/fedilabtube/InstancePickerActivity.java index a16cc16..0d0190f 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/InstancePickerActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/InstancePickerActivity.java @@ -48,7 +48,7 @@ import app.fedilab.fedilabtube.helper.RoundedBackgroundSpan; import app.fedilab.fedilabtube.viewmodel.InstancesVM; import es.dmoral.toasty.Toasty; -import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; public class InstancePickerActivity extends AppCompatActivity { diff --git a/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java b/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java index 78f3f47..cb9fac2 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java @@ -43,6 +43,8 @@ import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.Token; import app.fedilab.fedilabtube.databinding.ActivityLoginBinding; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperAcadInstance; +import app.fedilab.fedilabtube.helper.HelperInstance; import es.dmoral.toasty.Toasty; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.updateCredential; @@ -55,6 +57,7 @@ public class LoginActivity extends AppCompatActivity { private static String client_secret; private ActivityLoginBinding binding; + @SuppressLint("SetTextI18n") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -66,7 +69,13 @@ public class LoginActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); - SpannableString content_create = new SpannableString(getString(R.string.join_peertube)); + SpannableString content_create; + if (BuildConfig.FLAVOR.compareTo("queermotion") == 0) { + content_create = new SpannableString(getString(R.string.register_account)); + } else { + content_create = new SpannableString(getString(R.string.join_peertube)); + } + content_create.setSpan(new UnderlineSpan(), 0, content_create.length(), 0); content_create.setSpan(new ForegroundColorSpan(ContextCompat.getColor(LoginActivity.this, Helper.getColorAccent())), 0, content_create.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); @@ -80,10 +89,12 @@ public class LoginActivity extends AppCompatActivity { }); - if (BuildConfig.full_instances) { + if (BuildConfig.full_instances && BuildConfig.instance_switcher) { binding.loginInstanceContainer.setVisibility(View.VISIBLE); } - + if (BuildConfig.FLAVOR.compareTo("queermotion") == 0) { + binding.loginInstance.setText("queermotion.org"); + } if (Helper.isTablet(LoginActivity.this)) { @@ -106,7 +117,7 @@ public class LoginActivity extends AppCompatActivity { if (!hasFocus) { if (binding.loginUid.getText() != null && android.util.Patterns.EMAIL_ADDRESS.matcher(binding.loginUid.getText().toString().trim()).matches()) { String[] emailArray = binding.loginUid.getText().toString().split("@"); - if (emailArray.length > 1 && Arrays.asList(Helper.openid).contains(emailArray[1])) { + if (emailArray.length > 1 && Arrays.asList(HelperAcadInstance.openid).contains(emailArray[1])) { binding.loginButton.callOnClick(); } } @@ -125,13 +136,13 @@ public class LoginActivity extends AppCompatActivity { String instance, host; if (!BuildConfig.full_instances) { String[] emailArray = binding.loginUid.getText().toString().split("@"); - if (emailArray.length > 1 && !Arrays.asList(Helper.valideEmails).contains(emailArray[1])) { + if (emailArray.length > 1 && !Arrays.asList(HelperAcadInstance.valideEmails).contains(emailArray[1])) { Toasty.error(LoginActivity.this, getString(R.string.email_error_domain, emailArray[1])).show(); binding.loginButton.setEnabled(true); return; } host = emailArray[1]; - instance = Helper.getPeertubeUrl(host); + instance = HelperInstance.getPeertubeUrl(host); } else { if (binding.loginInstance.getText() == null || binding.loginInstance.getText().toString().trim().length() == 0) { Toasty.error(LoginActivity.this, getString(R.string.not_valide_instance)).show(); @@ -165,7 +176,7 @@ public class LoginActivity extends AppCompatActivity { } String finalInstance = instance; String finalHost = host; - if (Arrays.asList(Helper.openid).contains(host) && !BuildConfig.full_instances) { + if (Arrays.asList(HelperAcadInstance.openid).contains(host) && !BuildConfig.full_instances) { new Thread(() -> { try { Oauth oauth = new RetrofitPeertubeAPI(LoginActivity.this, finalInstance, null).oauthClient(null, null, null, null); @@ -185,7 +196,7 @@ public class LoginActivity extends AppCompatActivity { editor.apply(); Intent intent = new Intent(LoginActivity.this, WebviewConnectActivity.class); Bundle b = new Bundle(); - b.putString("url", "https://" + Helper.getPeertubeUrl(finalHost) + "/plugins/auth-openid-connect/0.0.1/auth/openid-connect"); + b.putString("url", "https://" + HelperInstance.getPeertubeUrl(finalHost) + "/plugins/auth-openid-connect/0.0.1/auth/openid-connect"); intent.putExtras(b); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); diff --git a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java index b2d0c83..487083b 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java @@ -16,19 +16,21 @@ package app.fedilab.fedilabtube; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -39,12 +41,23 @@ import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.kobakei.ratethisapp.RateThisApp; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; @@ -55,6 +68,7 @@ import java.util.regex.Pattern; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; import app.fedilab.fedilabtube.client.data.InstanceData; +import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.PeertubeInformation; @@ -62,9 +76,11 @@ import app.fedilab.fedilabtube.client.entities.Token; import app.fedilab.fedilabtube.client.entities.UserMe; import app.fedilab.fedilabtube.client.entities.UserSettings; import app.fedilab.fedilabtube.client.entities.WellKnownNodeinfo; +import app.fedilab.fedilabtube.databinding.ActivityMainBinding; import app.fedilab.fedilabtube.fragment.DisplayOverviewFragment; import app.fedilab.fedilabtube.fragment.DisplayVideosFragment; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperInstance; import app.fedilab.fedilabtube.helper.PlaylistExportHelper; import app.fedilab.fedilabtube.helper.SwitchAccountHelper; import app.fedilab.fedilabtube.services.RetrieveInfoService; @@ -73,63 +89,40 @@ import app.fedilab.fedilabtube.sqlite.Sqlite; import app.fedilab.fedilabtube.sqlite.StoredInstanceDAO; import app.fedilab.fedilabtube.viewmodel.TimelineVM; import es.dmoral.toasty.Toasty; +import su.litvak.chromecast.api.v2.ChromeCast; +import su.litvak.chromecast.api.v2.ChromeCasts; +import su.litvak.chromecast.api.v2.ChromeCastsListener; +import su.litvak.chromecast.api.v2.MediaStatus; import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.NORMAL; import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.SURFING; -import static app.fedilab.fedilabtube.helper.Helper.academies; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; +import static app.fedilab.fedilabtube.helper.HelperAcadInstance.academies; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements ChromeCastsListener { - public static PeertubeInformation peertubeInformation; public static int PICK_INSTANCE = 5641; public static int PICK_INSTANCE_SURF = 5642; public static UserMe userMe; + public static InstanceData.InstanceConfig instanceConfig; public static TypeOfConnection typeOfConnection; - final FragmentManager fm = getSupportFragmentManager(); - Fragment active; private DisplayVideosFragment recentFragment, locaFragment, trendingFragment, subscriptionFragment, mostLikedFragment; private DisplayOverviewFragment overviewFragment; - private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener - = item -> { - DisplayVideosFragment displayVideosFragment = null; - int itemId = item.getItemId(); - if (itemId == R.id.navigation_subscription) { - displayVideosFragment = subscriptionFragment; - setTitleCustom(R.string.subscriptions); - } else if (itemId == R.id.navigation_trending) { - setTitleCustom(R.string.title_trending); - displayVideosFragment = trendingFragment; - } else if (itemId == R.id.navigation_most_liked) { - setTitleCustom(R.string.title_most_liked); - displayVideosFragment = mostLikedFragment; - } else if (itemId == R.id.navigation_recently_added) { - setTitleCustom(R.string.title_recently_added); - displayVideosFragment = recentFragment; - } else if (itemId == R.id.navigation_local) { - setTitleCustom(R.string.title_local); - displayVideosFragment = locaFragment; - } else if (itemId == R.id.navigation_discover) { - setTitleCustom(R.string.title_discover); - fm.beginTransaction().hide(active).show(overviewFragment).commit(); - active = overviewFragment; - return true; - } - if (displayVideosFragment != null) { - fm.beginTransaction().hide(active).show(displayVideosFragment).commit(); - active = displayVideosFragment; - return true; - } else { - return false; - } - }; + private ActivityMainBinding binding; + private BroadcastReceiver manage_chromecast; + public static List chromeCasts; + public static ChromeCast chromeCast; + private VideoData.Video castedTube; + + public static boolean chromecastActivated = false; @SuppressLint("ApplySharedPref") public static void showRadioButtonDialogFullInstances(Activity activity, boolean storeInDb) { final SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); AlertDialog.Builder alt_bld = new AlertDialog.Builder(activity); alt_bld.setTitle(R.string.instance_choice); - String instance = Helper.getLiveInstance(activity); + String instance = HelperInstance.getLiveInstance(activity); final EditText input = new EditText(activity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, @@ -141,6 +134,12 @@ public class MainActivity extends AppCompatActivity { (dialog, which) -> new Thread(() -> { try { String newInstance = input.getText().toString().trim(); + if (!newInstance.startsWith("http")) { + newInstance = "http://" + newInstance; + } + URL url = new URL(newInstance); + newInstance = url.getHost(); + WellKnownNodeinfo.NodeInfo instanceNodeInfo = new RetrofitPeertubeAPI(activity, newInstance, null).getNodeInfo(); if (instanceNodeInfo.getSoftware() != null && instanceNodeInfo.getSoftware().getName().trim().toLowerCase().compareTo("peertube") == 0) { SharedPreferences.Editor editor = sharedpreferences.edit(); @@ -186,13 +185,156 @@ public class MainActivity extends AppCompatActivity { private void setTitleCustom(int titleRId) { Toolbar toolbar = findViewById(R.id.toolbar); TextView mTitle = toolbar.findViewById(R.id.toolbar_title); - mTitle.setText(getString(titleRId)); + if (mTitle != null) { + mTitle.setText(getString(titleRId)); + } } + private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener + = item -> { + int itemId = item.getItemId(); + if (itemId == R.id.navigation_discover) { + setTitleCustom(R.string.title_discover); + binding.viewpager.setCurrentItem(0); + } else if (itemId == R.id.navigation_subscription) { + binding.viewpager.setCurrentItem(1); + setTitleCustom(R.string.subscriptions); + } else if (itemId == R.id.navigation_trending) { + setTitleCustom(R.string.title_trending); + if (Helper.isLoggedIn(MainActivity.this)) { + binding.viewpager.setCurrentItem(2); + } else { + binding.viewpager.setCurrentItem(1); + } + } else if (itemId == R.id.navigation_most_liked) { + setTitleCustom(R.string.title_most_liked); + binding.viewpager.setCurrentItem(2); + } else if (itemId == R.id.navigation_recently_added) { + setTitleCustom(R.string.title_recently_added); + binding.viewpager.setCurrentItem(3); + } else if (itemId == R.id.navigation_local) { + setTitleCustom(R.string.title_local); + binding.viewpager.setCurrentItem(4); + } + return true; + }; + + @Override + public void newChromeCastDiscovered(ChromeCast chromeCast) { + if (chromeCasts == null) { + chromeCasts = new ArrayList<>(); + chromeCasts.add(chromeCast); + } else { + boolean canBeAdded = true; + for (ChromeCast cast : chromeCasts) { + if (cast.getName().compareTo(chromeCast.getName()) == 0) { + canBeAdded = false; + break; + } + } + if (canBeAdded) { + chromeCasts.add(chromeCast); + } + } + try { + if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) { + if (binding.castInfo.getVisibility() == View.GONE) { + binding.castInfo.setVisibility(View.VISIBLE); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Override + public void chromeCastRemoved(ChromeCast chromeCast) { + + } + + @Override + public void onDestroy() { + super.onDestroy(); + binding = null; + ChromeCasts.unregisterListener(this); + if (manage_chromecast != null) { + LocalBroadcastManager.getInstance(MainActivity.this).unregisterReceiver(manage_chromecast); + + new Thread(() -> { + if (chromeCasts != null && chromeCasts.size() > 0) { + for (ChromeCast cast : chromeCasts) { + try { + cast.stopApp(); + cast.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }).start(); + } + if (chromeCasts != null) { + chromeCasts = null; + } + if (chromeCast != null) { + chromeCast = null; + } + } + + //Method for discovering cast devices + public void discoverCast() { + new Thread(() -> { + if (chromeCasts != null) { + for (ChromeCast cast : chromeCasts) { + try { + cast.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + } + chromeCasts = null; + } + chromeCasts = new ArrayList<>(); + try { + List interfaces; + interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); + for (NetworkInterface ni : interfaces) { + if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) { + Enumeration inetAddressEnumeration = ni.getInetAddresses(); + while (inetAddressEnumeration.hasMoreElements()) { + InetAddress inetAddress = inetAddressEnumeration.nextElement(); + ChromeCasts.restartDiscovery(inetAddress); + int tryFind = 0; + while (ChromeCasts.get().isEmpty() && tryFind < 5) { + try { + Thread.sleep(1000); + tryFind++; + } catch (InterruptedException ignored) { + } + } + } + } + } + ChromeCasts.stopDiscovery(); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = this::invalidateOptionsMenu; + mainHandler.post(myRunnable); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + binding = ActivityMainBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + ChromeCastsListener chromeCastsListener = this; + ChromeCasts.registerListener(chromeCastsListener); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -208,22 +350,6 @@ public class MainActivity extends AppCompatActivity { } checkIfConnectedUsers(); - Fragment fragment = getSupportFragmentManager().findFragmentByTag("5"); - if (fragment != null) - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - fragment = getSupportFragmentManager().findFragmentByTag("4"); - if (fragment != null) - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - fragment = getSupportFragmentManager().findFragmentByTag("3"); - if (fragment != null) - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - fragment = getSupportFragmentManager().findFragmentByTag("2"); - if (fragment != null) - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - fragment = getSupportFragmentManager().findFragmentByTag("1"); - if (fragment != null) - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - recentFragment = new DisplayVideosFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.RECENT); @@ -250,99 +376,48 @@ public class MainActivity extends AppCompatActivity { mostLikedFragment.setArguments(bundle); overviewFragment = new DisplayOverviewFragment(); - - if (active == null) { - active = overviewFragment; - } - - if (!Helper.isLoggedIn(MainActivity.this)) { - fm.beginTransaction().add(R.id.nav_host_fragment, locaFragment, "5").hide(locaFragment).commit(); - fm.beginTransaction().add(R.id.nav_host_fragment, recentFragment, "4").hide(recentFragment).commit(); - fm.beginTransaction().add(R.id.nav_host_fragment, mostLikedFragment, "3").hide(mostLikedFragment).commit(); - fm.beginTransaction().add(R.id.nav_host_fragment, trendingFragment, "2").hide(trendingFragment).commit(); - fm.beginTransaction().add(R.id.nav_host_fragment, overviewFragment, "1").commit(); + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + binding.viewpager.setAdapter(mPagerAdapter); } + binding.viewpager.setOffscreenPageLimit(5); + + + binding.viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + MenuItem item = binding.navView.getMenu().getItem(position); + binding.navView.setSelectedItemId(item.getItemId()); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + toolbar.setOnClickListener(v -> { - if (active instanceof DisplayVideosFragment) { - ((DisplayVideosFragment) active).scrollToTop(); - } else if (active instanceof DisplayOverviewFragment) { - ((DisplayOverviewFragment) active).scrollToTop(); + if (binding.viewpager.getAdapter() == null) { + return; + } + if (binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem()) instanceof DisplayVideosFragment) { + ((DisplayVideosFragment) binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem())).scrollToTop(); + } else if (binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem()) instanceof DisplayOverviewFragment) { + ((DisplayOverviewFragment) binding.viewpager.getAdapter().instantiateItem(binding.viewpager, binding.viewpager.getCurrentItem())).scrollToTop(); } }); setTitleCustom(R.string.title_discover); if (Helper.isLoggedIn(MainActivity.this)) { - navView.inflateMenu(R.menu.bottom_nav_menu_connected); - new Thread(() -> { - final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - String tokenStr = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - String instance = Helper.getLiveInstance(MainActivity.this); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - String instanceShar = sharedpreferences.getString(Helper.PREF_INSTANCE, null); - String userIdShar = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - Account account = new AccountDAO(MainActivity.this, db).getAccountByToken(tokenStr); - if (account == null) { - account = new AccountDAO(MainActivity.this, db).getAccountByIdInstance(userIdShar, instanceShar); - } - if (account != null) { - Account finalAccount = account; - OauthParams oauthParams = new OauthParams(); - oauthParams.setGrant_type("refresh_token"); - oauthParams.setClient_id(account.getClient_id()); - oauthParams.setClient_secret(account.getClient_secret()); - oauthParams.setRefresh_token(account.getRefresh_token()); - oauthParams.setAccess_token(account.getToken()); - try { - Token token = new RetrofitPeertubeAPI(MainActivity.this).manageToken(oauthParams); - if (token == null && Helper.instanceOnline(instance)) { - runOnUiThread(() -> Helper.logoutCurrentUser(MainActivity.this, finalAccount)); - return; - } else if (token == null) { - return; - } - runOnUiThread(() -> { - //To avoid a token issue with subscriptions, adding fragment is done when the token is refreshed. - new Handler().post(() -> { - fm.beginTransaction().add(R.id.nav_host_fragment, locaFragment, "5").hide(locaFragment).commit(); - fm.beginTransaction().add(R.id.nav_host_fragment, recentFragment, "4").hide(recentFragment).commitAllowingStateLoss(); - fm.beginTransaction().add(R.id.nav_host_fragment, trendingFragment, "3").hide(trendingFragment).commitAllowingStateLoss(); - fm.beginTransaction().add(R.id.nav_host_fragment, subscriptionFragment, "2").hide(subscriptionFragment).commitAllowingStateLoss(); - fm.beginTransaction().add(R.id.nav_host_fragment, overviewFragment, "1").commitAllowingStateLoss(); - }); - }); - - userMe = new RetrofitPeertubeAPI(MainActivity.this, instance, token.getAccess_token()).verifyCredentials(); - if (userMe != null && userMe.getAccount() != null) { - new AccountDAO(MainActivity.this, db).updateAccount(userMe.getAccount()); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.PREF_KEY_ID, account.getId()); - editor.putString(Helper.PREF_KEY_NAME, account.getUsername()); - editor.putBoolean(getString(R.string.set_autoplay_choice), userMe.isAutoPlayVideo()); - editor.putBoolean(getString(R.string.set_store_in_history), userMe.isVideosHistoryEnabled()); - editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), userMe.isAutoPlayNextVideo()); - editor.putString(getString(R.string.set_video_sensitive_choice), userMe.getNsfwPolicy()); - //Sync languages from server - List videoLanguageServer = userMe.getVideoLanguages(); - if (videoLanguageServer != null) { - Set videoLanguageServerSet = new TreeSet<>(videoLanguageServer); - videoLanguageServerSet.addAll(videoLanguageServer); - Set videoLanguageLocal = sharedpreferences.getStringSet(getString(R.string.set_video_language_choice), null); - if (videoLanguageServerSet.size() > 0 && videoLanguageLocal != null) { - videoLanguageServer.addAll(videoLanguageLocal); - } - editor.putStringSet(getString(R.string.set_video_language_choice), videoLanguageServerSet); - editor.apply(); - } - } - } catch (Error error) { - runOnUiThread(() -> Helper.logoutCurrentUser(MainActivity.this, finalAccount)); - error.printStackTrace(); - } - } - }).start(); + refreshToken(); } else { navView.inflateMenu(R.menu.bottom_nav_menu); @@ -363,6 +438,100 @@ public class MainActivity extends AppCompatActivity { if (!BuildConfig.full_instances) { PlaylistExportHelper.manageIntentUrl(MainActivity.this, getIntent()); } + + binding.castClose.setOnClickListener(v -> { + Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); + Bundle b = new Bundle(); + b.putInt("displayed", 0); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intentBC); + }); + + binding.castTogglePlay.setOnClickListener(v -> { + if (chromeCast != null) { + new Thread(() -> { + try { + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE); + mainHandler.post(myRunnable); + int icon = -1; + if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { + chromeCast.pause(); + icon = R.drawable.ic_baseline_play_arrow_32; + } else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { + chromeCast.play(); + icon = R.drawable.ic_baseline_pause_32; + } + if (icon != -1) { + int finalIcon = icon; + myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon); + mainHandler.post(myRunnable); + } + myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE); + mainHandler.post(myRunnable); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + }); + manage_chromecast = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = intent.getExtras(); + assert b != null; + int state = b.getInt("state_asked", -1); + int displayed = b.getInt("displayed", -1); + castedTube = b.getParcelable("castedTube"); + + if (state == 1) { + discoverCast(); + } else if (state == 0) { + new Thread(() -> { + try { + if (chromeCast != null) { + chromeCast.stopApp(); + chromeCast.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + if (displayed == 1) { + chromecastActivated = true; + if (castedTube != null) { + binding.castInfo.setVisibility(View.VISIBLE); + Helper.loadGiF(MainActivity.this, castedTube.getThumbnailPath(), binding.castView); + binding.castTitle.setText(castedTube.getTitle()); + binding.castDescription.setText(castedTube.getDescription()); + } + } else if (displayed == 0) { + chromecastActivated = false; + binding.castInfo.setVisibility(View.GONE); + new Thread(() -> { + try { + if (chromeCast != null) { + chromeCast.stopApp(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + } + }; + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + + LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS)); + int search_cast = sharedpreferences.getInt(getString(R.string.set_cast_choice), 0); + if (search_cast == 1) { + discoverCast(); + } + } + + public DisplayVideosFragment getSubscriptionFragment() { + return subscriptionFragment; } private void startInForeground() { @@ -374,6 +543,88 @@ public class MainActivity extends AppCompatActivity { } } + private void refreshToken() { + new Thread(() -> { + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String tokenStr = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + String instance = HelperInstance.getLiveInstance(MainActivity.this); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + String instanceShar = sharedpreferences.getString(Helper.PREF_INSTANCE, null); + String userIdShar = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Account account = new AccountDAO(MainActivity.this, db).getAccountByToken(tokenStr); + if (account == null) { + account = new AccountDAO(MainActivity.this, db).getAccountByIdInstance(userIdShar, instanceShar); + } + if (account != null) { + Account finalAccount = account; + OauthParams oauthParams = new OauthParams(); + oauthParams.setGrant_type("refresh_token"); + oauthParams.setClient_id(account.getClient_id()); + oauthParams.setClient_secret(account.getClient_secret()); + oauthParams.setRefresh_token(account.getRefresh_token()); + oauthParams.setAccess_token(account.getToken()); + try { + Token token = new RetrofitPeertubeAPI(MainActivity.this).manageToken(oauthParams); + if (token == null) { + return; + } + runOnUiThread(() -> { + //To avoid a token issue with subscriptions, adding fragment is done when the token is refreshed. + new Handler().post(() -> { + if (Helper.isLoggedIn(MainActivity.this)) { + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + binding.viewpager.setAdapter(mPagerAdapter); + } + }); + }); + + userMe = new RetrofitPeertubeAPI(MainActivity.this, instance, token.getAccess_token()).verifyCredentials(); + if (userMe != null && userMe.getAccount() != null) { + new AccountDAO(MainActivity.this, db).updateAccount(userMe.getAccount()); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.PREF_KEY_ID, account.getId()); + editor.putString(Helper.PREF_KEY_NAME, account.getUsername()); + editor.putBoolean(getString(R.string.set_autoplay_choice), userMe.isAutoPlayVideo()); + editor.putBoolean(getString(R.string.set_store_in_history), userMe.isVideosHistoryEnabled()); + editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), userMe.isAutoPlayNextVideo()); + editor.putString(getString(R.string.set_video_sensitive_choice), userMe.getNsfwPolicy()); + //Sync languages from server + List videoLanguageServer = userMe.getVideoLanguages(); + if (videoLanguageServer != null) { + Set videoLanguageServerSet = new TreeSet<>(videoLanguageServer); + videoLanguageServerSet.addAll(videoLanguageServer); + Set videoLanguageLocal = sharedpreferences.getStringSet(getString(R.string.set_video_language_choice), null); + if (videoLanguageServerSet.size() > 0 && videoLanguageLocal != null) { + videoLanguageServer.addAll(videoLanguageLocal); + } + editor.putStringSet(getString(R.string.set_video_language_choice), videoLanguageServerSet); + editor.apply(); + } + } + instanceConfig = new RetrofitPeertubeAPI(MainActivity.this).getConfigInstance(); + } catch (Error error) { + runOnUiThread(() -> { + AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); + alt_bld.setTitle(R.string.refresh_token_failed); + alt_bld.setMessage(R.string.refresh_token_failed_message); + alt_bld.setNegativeButton(R.string.action_logout, (dialog, id) -> { + dialog.dismiss(); + Helper.logoutCurrentUser(MainActivity.this, finalAccount); + }); + alt_bld.setPositiveButton(R.string._retry, (dialog, id) -> { + dialog.dismiss(); + refreshToken(); + }); + AlertDialog alert = alt_bld.create(); + alert.show(); + + }); + error.printStackTrace(); + } + } + }).start(); + } + @Override public boolean onCreateOptionsMenu(@NotNull Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); @@ -383,7 +634,7 @@ public class MainActivity extends AppCompatActivity { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { - Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$"); + Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$"); Matcher matcherLink = link.matcher(query.trim()); if (matcherLink.find()) { Intent intent = new Intent(MainActivity.this, PeertubeActivity.class); @@ -422,18 +673,17 @@ public class MainActivity extends AppCompatActivity { MenuItem instanceItem = menu.findItem(R.id.action_change_instance); MenuItem accountItem = menu.findItem(R.id.action_account); - Toolbar toolbar = findViewById(R.id.toolbar); - ImageView instances = toolbar.findViewById(R.id.instances); - if (BuildConfig.full_instances && ((Helper.isLoggedIn(MainActivity.this) && typeOfConnection == NORMAL) || typeOfConnection == SURFING)) { - instances.setVisibility(View.VISIBLE); - instances.setOnClickListener(null); - instances.setOnClickListener(v -> { + + if (BuildConfig.surfing_mode && ((Helper.isLoggedIn(MainActivity.this) && typeOfConnection == NORMAL) || typeOfConnection == SURFING)) { + binding.instances.setVisibility(View.VISIBLE); + binding.instances.setOnClickListener(null); + binding.instances.setOnClickListener(v -> { Intent intent = new Intent(MainActivity.this, ManageInstancesActivity.class); startActivity(intent); overridePendingTransition(R.anim.slide_in_up, R.anim.slide_out_up); }); } else { - instances.setVisibility(View.GONE); + binding.instances.setVisibility(View.GONE); } switch (typeOfConnection) { case UNKNOWN: @@ -485,13 +735,17 @@ public class MainActivity extends AppCompatActivity { break; } + if (!BuildConfig.instance_switcher) { + instanceItem.setVisible(false); + } - if (!BuildConfig.full_instances) { + if (!BuildConfig.sepia_search) { sepiaSearchItem.setVisible(false); } return true; } + private void checkIfConnectedUsers() { new Thread(() -> { try { @@ -601,13 +855,6 @@ public class MainActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } - public void setActive(DisplayVideosFragment displayVideosFragment) { - this.active = displayVideosFragment; - } - - public void setSubscriptionFragment(DisplayVideosFragment displayVideosFragment) { - this.subscriptionFragment = displayVideosFragment; - } @Override protected void onNewIntent(Intent intent) { @@ -631,10 +878,10 @@ public class MainActivity extends AppCompatActivity { AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); alt_bld.setTitle(R.string.instance_choice); final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - String acad = Helper.getLiveInstance(MainActivity.this); + String acad = HelperInstance.getLiveInstance(MainActivity.this); int i = 0; for (String item : academies) { - if (Helper.getPeertubeUrl(item).compareTo(acad) == 0) { + if (HelperInstance.getPeertubeUrl(item).compareTo(acad) == 0) { break; } i++; @@ -662,11 +909,55 @@ public class MainActivity extends AppCompatActivity { SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.PREF_INSTANCE, String.valueOf(data.getData())); editor.commit(); - finish(); + recreate(); } } } + private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NotNull + @Override + public Fragment getItem(final int position) { + if (Helper.isLoggedIn(MainActivity.this)) { + switch (position) { + case 0: + return overviewFragment; + case 1: + return subscriptionFragment; + case 2: + return trendingFragment; + case 3: + return recentFragment; + case 4: + return locaFragment; + } + } else { + switch (position) { + case 0: + return overviewFragment; + case 1: + return trendingFragment; + case 2: + return mostLikedFragment; + case 3: + return recentFragment; + case 4: + return locaFragment; + } + } + return overviewFragment; + } + + @Override + public int getCount() { + return 5; + } + } public enum TypeOfConnection { UNKNOWN, diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java index d30280c..86dce98 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeActivity.java @@ -17,7 +17,6 @@ package app.fedilab.fedilabtube; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.Dialog; import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.Context; @@ -29,6 +28,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.PorterDuff; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; @@ -37,19 +37,23 @@ import android.os.Handler; import android.os.Looper; import android.support.v4.media.session.MediaSessionCompat; import android.text.Html; +import android.text.SpannableString; import android.text.Spanned; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; import android.util.DisplayMetrics; import android.view.LayoutInflater; +import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.view.inputmethod.InputMethodManager; -import android.widget.ArrayAdapter; +import android.webkit.MimeTypeMap; import android.widget.EditText; -import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -66,6 +70,7 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.lifecycle.ViewModelProvider; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -79,6 +84,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; @@ -100,8 +106,12 @@ import com.google.android.exoplayer2.video.VideoListener; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.text.DateFormat; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -109,6 +119,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.MenuItemVideo; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; import app.fedilab.fedilabtube.client.data.CaptionData.Caption; @@ -116,15 +127,19 @@ import app.fedilab.fedilabtube.client.data.CommentData; import app.fedilab.fedilabtube.client.data.CommentData.Comment; import app.fedilab.fedilabtube.client.data.PlaylistData; import app.fedilab.fedilabtube.client.data.VideoData; +import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.File; -import app.fedilab.fedilabtube.client.entities.ItemStr; +import app.fedilab.fedilabtube.client.entities.MenuItemView; import app.fedilab.fedilabtube.client.entities.PlaylistExist; import app.fedilab.fedilabtube.client.entities.Report; +import app.fedilab.fedilabtube.client.entities.UserSettings; import app.fedilab.fedilabtube.databinding.ActivityPeertubeBinding; import app.fedilab.fedilabtube.drawer.CommentListAdapter; +import app.fedilab.fedilabtube.drawer.MenuAdapter; +import app.fedilab.fedilabtube.drawer.MenuItemAdapter; import app.fedilab.fedilabtube.helper.CacheDataSourceFactory; -import app.fedilab.fedilabtube.helper.FullScreenMediaController; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperInstance; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; import app.fedilab.fedilabtube.viewmodel.CaptionsVM; @@ -137,29 +152,34 @@ import app.fedilab.fedilabtube.webview.CustomWebview; import app.fedilab.fedilabtube.webview.MastalabWebChromeClient; import app.fedilab.fedilabtube.webview.MastalabWebViewClient; import es.dmoral.toasty.Toasty; +import su.litvak.chromecast.api.v2.ChromeCast; +import su.litvak.chromecast.api.v2.MediaStatus; +import su.litvak.chromecast.api.v2.Status; +import static app.fedilab.fedilabtube.MainActivity.chromeCast; +import static app.fedilab.fedilabtube.MainActivity.chromeCasts; +import static app.fedilab.fedilabtube.MainActivity.chromecastActivated; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.ADD_COMMENT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.RATEVIDEO; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPLY; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_ACCOUNT; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.REPORT_VIDEO; +import static app.fedilab.fedilabtube.helper.Helper.CAST_ID; import static app.fedilab.fedilabtube.helper.Helper.getAttColor; -import static app.fedilab.fedilabtube.helper.Helper.getLiveInstance; import static app.fedilab.fedilabtube.helper.Helper.isLoggedIn; import static app.fedilab.fedilabtube.helper.Helper.loadGiF; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO; -public class PeertubeActivity extends AppCompatActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, TorrentListener { +public class PeertubeActivity extends AppCompatActivity implements CommentListAdapter.AllCommentRemoved, Player.EventListener, VideoListener, TorrentListener, MenuAdapter.ItemClicked, MenuItemAdapter.ItemAction { public static String video_id; public static List playedVideos = new ArrayList<>(); private String peertubeInstance, videoUuid; - private FullScreenMediaController.fullscreen fullscreen; private ImageView fullScreenIcon; private SimpleExoPlayer player; private boolean fullScreenMode; - private Dialog fullScreenDialog; private VideoData.Video peertube; private int mode; private Map> playlists; @@ -182,6 +202,11 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd private String show_more_content; private videoOrientation videoOrientationType; private int initialOrientation; + private String currentResolution; + private String currentCaption; + private boolean isRemote; + private boolean willPlayFromIntent; + private String chromeCastVideoURL; public static void hideKeyboard(Activity activity) { if (activity != null && activity.getWindow() != null) { @@ -208,7 +233,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd @Override public void onStreamStarted(Torrent torrent) { - startStream(torrent.getVideoFile().getAbsolutePath(), null, autoPlay, -1, null, null); + startStream(torrent.getVideoFile().getAbsolutePath(), null, autoPlay, -1, null, null, true); } @Override @@ -232,7 +257,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityPeertubeBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); @@ -245,13 +269,15 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd Account account = new AccountDAO(PeertubeActivity.this, db).getAccountByToken(token); Helper.loadGiF(PeertubeActivity.this, account.getAvatar() != null ? account.getAvatar().getPath() : null, binding.myPp); } - + isRemote = false; TorrentOptions torrentOptions = new TorrentOptions.Builder() .saveLocation(getCacheDir()) .autoDownload(true) .removeFilesAfterStop(true) .build(); + + fullScreenMode = false; torrentStream = TorrentStream.init(torrentOptions); torrentStream.addListener(PeertubeActivity.this); initialOrientation = getResources().getConfiguration().orientation; @@ -283,14 +309,14 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd Bundle b = intent.getExtras(); if (b != null) { - peertubeInstance = b.getString("peertube_instance", Helper.getLiveInstance(PeertubeActivity.this)); + peertubeInstance = b.getString("peertube_instance", HelperInstance.getLiveInstance(PeertubeActivity.this)); videoUuid = b.getString("video_uuid", null); isMyVideo = b.getBoolean("isMyVideo", false); sepiaSearch = b.getBoolean("sepia_search", false); peertube = b.getParcelable("video"); } - manageIntentUrl(intent); + willPlayFromIntent = manageIntentUrl(intent); binding.peertubeDescriptionMore.setOnClickListener(v -> { if (show_more_content != null && peertube != null) { @@ -314,6 +340,11 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd playInMinimized = false; } + if (peertube != null && peertube.isNsfw()) { + binding.videoSensitive.setVisibility(View.VISIBLE); + } else { + binding.videoSensitive.setVisibility(View.INVISIBLE); + } if (mode == Helper.VIDEO_MODE_WEBVIEW) { binding.webviewVideo.setVisibility(View.VISIBLE); binding.mediaVideo.setVisibility(View.GONE); @@ -322,29 +353,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(PeertubeActivity.this, webview_video, binding.mainMediaFrame, binding.videoLayout); mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> { - if (fullscreen) { binding.videoLayout.setVisibility(View.VISIBLE); - WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; - attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - getWindow().setAttributes(attrs); - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - binding.peertubeInformationContainer.setVisibility(View.GONE); - if (videoOrientationType == videoOrientation.LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } } else { - WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; - attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - getWindow().setAttributes(attrs); - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); binding.videoLayout.setVisibility(View.GONE); - binding.peertubeInformationContainer.setVisibility(View.VISIBLE); } + toogleFullscreen(fullscreen); }); binding.webviewVideo.getSettings().setAllowFileAccess(true); binding.webviewVideo.setWebChromeClient(mastalabWebChromeClient); @@ -361,21 +375,23 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd if (mode != Helper.VIDEO_MODE_WEBVIEW) { binding.doubleTapPlayerView.setControllerShowTimeoutMs(1000); binding.doubleTapPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); - initFullscreenDialog(); - initFullscreenButton(); + initControllerButtons(); binding.doubleTapPlayerView .setDoubleTapDelay(500); binding.doubleTapPlayerView.setDoubleTapEnabled(true); + binding.doubleTapPlayerView.setControllerShowTimeoutMs(0); binding.mediaVideo.performListener(new YouTubeOverlay.PerformListener() { @Override public void onAnimationStart() { binding.mediaVideo.setVisibility(View.VISIBLE); + binding.doubleTapPlayerView.setUseController(false); } @Override public void onAnimationEnd() { binding.mediaVideo.setVisibility(View.GONE); + binding.doubleTapPlayerView.setUseController(true); } }).playerView(binding.doubleTapPlayerView).seekSeconds(10); binding.doubleTapPlayerView.setPlayer(player); @@ -389,7 +405,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.closeReply.setOnClickListener(v -> closeCommentThread()); binding.closePost.setOnClickListener(v -> closePostComment()); - commentListAdapter = new CommentListAdapter(comments, isMyVideo || Helper.isVideoOwner(PeertubeActivity.this, peertube), false); + commentListAdapter = new CommentListAdapter(comments, isMyVideo || Helper.isVideoOwner(PeertubeActivity.this, peertube), false, peertubeInstance, sepiaSearch); commentListAdapter.allCommentRemoved = PeertubeActivity.this; LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); binding.peertubeComments.setLayoutManager(mLayoutManager); @@ -410,22 +426,58 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } } }); - if (peertube != null && sepiaSearch && peertube.getEmbedUrl() != null && Helper.isLoggedIn(PeertubeActivity.this)) { + if (!willPlayFromIntent && peertube != null && sepiaSearch && peertube.getEmbedUrl() != null && Helper.isLoggedIn(PeertubeActivity.this)) { SearchVM viewModelSearch = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class); - viewModelSearch.getVideos("0", peertube.getEmbedUrl()).observe(PeertubeActivity.this, this::manageVIewVideos); + viewModelSearch.getVideos("0", peertube.getUuid()).observe(PeertubeActivity.this, this::manageVIewVideos); } else { playVideo(); } + registBroadcastReceiver(); if (autoFullscreen && autoPlay) { openFullscreenDialog(); - if (videoOrientationType == videoOrientation.LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } } - binding.postCommentButton.setOnClickListener(v -> openPostComment(null, 0)); + binding.postCommentButton.setOnClickListener(v -> { + if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { + openPostComment(null, 0); + } else { + if (sepiaSearch) { + Toasty.info(PeertubeActivity.this, getString(R.string.federation_issue), Toasty.LENGTH_SHORT).show(); + } else { + Toasty.error(PeertubeActivity.this, getString(R.string.not_logged_in), Toast.LENGTH_SHORT).show(); + } + } + }); + + binding.castPlay.setOnClickListener(v -> { + binding.castLoader.setVisibility(View.VISIBLE); + if (chromeCast != null) { + new Thread(() -> { + try { + int icon = -1; + if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { + chromeCast.pause(); + icon = R.drawable.ic_baseline_play_arrow_32; + } else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) { + chromeCast.play(); + icon = R.drawable.ic_baseline_pause_32; + } + if (icon != -1) { + Handler mainHandler = new Handler(Looper.getMainLooper()); + int finalIcon = icon; + Runnable myRunnable = () -> binding.castPlay.setImageResource(finalIcon); + mainHandler.post(myRunnable); + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> binding.castLoader.setVisibility(View.GONE); + mainHandler.post(myRunnable); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + }); + } private void manageVIewVideos(APIResponse apiResponse) { @@ -434,7 +486,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd return; } peertube = apiResponse.getPeertubes().get(0); - if (peertube.getUserHistory() != null) { + if (peertube.isNsfw()) { + binding.videoSensitive.setVisibility(View.VISIBLE); + } else { + binding.videoSensitive.setVisibility(View.INVISIBLE); + } + if (player != null && peertube.getUserHistory() != null) { player.seekTo(peertube.getUserHistory().getCurrentTime() * 1000); } sepiaSearch = false; @@ -477,7 +534,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd comment.setInReplyToCommentId(null); comment.setTotalReplies(0); commentsThread.add(0, comment); - commentReplyListAdapter = new CommentListAdapter(commentsThread, Helper.isVideoOwner(PeertubeActivity.this, peertube), true); + commentReplyListAdapter = new CommentListAdapter(commentsThread, Helper.isVideoOwner(PeertubeActivity.this, peertube), true, peertubeInstance, sepiaSearch); LinearLayoutManager mLayoutManager = new LinearLayoutManager(PeertubeActivity.this); binding.peertubeReply.setLayoutManager(mLayoutManager); binding.peertubeReply.setNestedScrollingEnabled(false); @@ -506,8 +563,10 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd super.onNewIntent(intent); Bundle b = intent.getExtras(); if (b != null) { - peertubeInstance = b.getString("peertube_instance", Helper.getLiveInstance(PeertubeActivity.this)); + isRemote = false; + peertubeInstance = b.getString("peertube_instance", HelperInstance.getLiveInstance(PeertubeActivity.this)); videoUuid = b.getString("video_uuid", null); + setRequestedOrientationCustom(initialOrientation); if (comments != null && comments.size() > 0) { int number = comments.size(); comments.clear(); @@ -515,17 +574,35 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } playVideo(); } - manageIntentUrl(intent); + willPlayFromIntent = manageIntentUrl(intent); } - private void manageIntentUrl(Intent intent) { + private boolean manageIntentUrl(Intent intent) { if (intent.getData() != null) { //Comes from a link String url = intent.getData().toString(); - Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$"); + Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$"); Matcher matcherLink = link.matcher(url); if (matcherLink.find()) { String instance = matcherLink.group(1); String uuid = matcherLink.group(2); + String hour = matcherLink.group(4); + String min = matcherLink.group(5); + String sec = matcherLink.group(6); + int hourInt, minInt, secInt; + int totalSeconds = 0; + if (hour != null) { + hourInt = Integer.parseInt(hour.replace("h", "")); + totalSeconds += 3600 * hourInt; + } + if (min != null) { + minInt = Integer.parseInt(min.replace("m", "")); + totalSeconds += 60 * minInt; + } + if (sec != null) { + secInt = Integer.parseInt(sec.replace("s", "")); + totalSeconds += secInt; + } + if (instance != null && uuid != null) { peertubeInstance = instance.replace("https://", "").replace("http://", ""); sepiaSearch = true; // Sepia search flag is used because, at this time we don't know if the video is federated. @@ -533,8 +610,24 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd peertube = new VideoData.Video(); peertube.setUuid(uuid); peertube.setEmbedUrl(url); - SearchVM viewModelSearch = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class); - viewModelSearch.getVideos("0", peertube.getEmbedUrl()).observe(PeertubeActivity.this, this::manageVIewVideos); + if (totalSeconds > 0) { + VideoData.UserHistory userHistory = new VideoData.UserHistory(); + userHistory.setCurrentTime(totalSeconds); + peertube.setUserHistory(userHistory); + } + TimelineVM viewModelTimeline = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class); + viewModelTimeline.getVideo(peertubeInstance, peertube.getUuid(), false).observe(PeertubeActivity.this, this::manageVIewVideo); + if (player != null) { + player.release(); + } + if (comments != null && comments.size() > 0) { + int number = comments.size(); + comments.clear(); + commentListAdapter.notifyItemRangeRemoved(0, number); + } + fetchComments(); + isRemote = true; + return true; } else { Helper.forwardToAnotherApp(PeertubeActivity.this, intent); finish(); @@ -544,6 +637,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd finish(); } } + return false; } private void playVideo() { @@ -554,52 +648,54 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.doubleTapPlayerView.setPlayer(player); binding.loader.setVisibility(View.GONE); player.setPlayWhenReady(autoPlay); + if (autoPlay) { + binding.doubleTapPlayerView.hideController(); + } captions = null; } + currentResolution = null; show_more_content = null; + currentCaption = "null"; binding.peertubeDescriptionMore.setVisibility(View.GONE); + if (autoFullscreen && autoPlay) { - fullscreen = FullScreenMediaController.fullscreen.ON; - setFullscreen(FullScreenMediaController.fullscreen.ON); - fullScreenMode = true; openFullscreenDialog(); - if (videoOrientationType == videoOrientation.LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - } else { - fullscreen = FullScreenMediaController.fullscreen.OFF; - setFullscreen(FullScreenMediaController.fullscreen.OFF); - fullScreenMode = false; } binding.peertubePlaylist.setVisibility(View.VISIBLE); - binding.peertubeBookmark.setVisibility(View.GONE); TimelineVM feedsViewModel = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class); - feedsViewModel.getVideo(sepiaSearch ? peertubeInstance : null, videoUuid, isMyVideo).observe(PeertubeActivity.this, this::manageVIewVideo); + if (!isRemote) { + feedsViewModel.getVideo(sepiaSearch ? peertubeInstance : null, videoUuid, isMyVideo).observe(PeertubeActivity.this, this::manageVIewVideo); + } CaptionsVM captionsViewModel = new ViewModelProvider(PeertubeActivity.this).get(CaptionsVM.class); captionsViewModel.getCaptions(sepiaSearch ? peertubeInstance : null, videoUuid).observe(PeertubeActivity.this, this::manageCaptions); new Thread(() -> { try { - RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(PeertubeActivity.this); + RetrofitPeertubeAPI api; + if (peertubeInstance != null) { + api = new RetrofitPeertubeAPI(PeertubeActivity.this, peertubeInstance, null); + } else { + api = new RetrofitPeertubeAPI(PeertubeActivity.this); + } VideoData.Description description = api.getVideoDescription(videoUuid); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { - if (description == null) { - binding.peertubeDescriptionMore.setVisibility(View.GONE); - show_more_content = null; - } else { - if (!PeertubeActivity.this.isFinishing()) { - if (peertube != null && peertube.getDescription() != null && description.getDescription() != null && description.getDescription().compareTo(peertube.getDescription()) > 0) { - binding.peertubeDescriptionMore.setVisibility(View.VISIBLE); - show_more_content = description.getDescription(); - } else { - binding.peertubeDescriptionMore.setVisibility(View.GONE); - show_more_content = null; + if (!isFinishing()) { + if (description == null) { + binding.peertubeDescriptionMore.setVisibility(View.GONE); + show_more_content = null; + } else { + if (!PeertubeActivity.this.isFinishing()) { + if (peertube != null && ((peertube.getDescription() == null && description.getDescription() != null && description.getDescription().trim().length() > 0) || (peertube.getDescription() != null && description.getDescription() != null + && description.getDescription().compareTo(peertube.getDescription()) > 0))) { + binding.peertubeDescriptionMore.setVisibility(View.VISIBLE); + show_more_content = description.getDescription(); + } else { + binding.peertubeDescriptionMore.setVisibility(View.GONE); + show_more_content = null; + } } } } - }; mainHandler.post(myRunnable); } catch (Exception e) { @@ -608,18 +704,19 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd }).start(); } - public void change() { - if (fullscreen == FullScreenMediaController.fullscreen.ON) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | - WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - Objects.requireNonNull(getSupportActionBar()).hide(); - binding.peertubeInformationContainer.setVisibility(View.GONE); - } else { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); - Objects.requireNonNull(getSupportActionBar()).show(); - binding.peertubeInformationContainer.setVisibility(View.VISIBLE); + @Override + public boolean onCreateOptionsMenu(@NotNull Menu menu) { + getMenuInflater().inflate(R.menu.video_menu, menu); + MenuItem castItem = menu.findItem(R.id.action_cast); + if (chromeCasts != null && chromeCasts.size() > 0) { + castItem.setVisible(true); + if (chromeCast != null && chromeCast.isConnected()) { + castItem.setIcon(R.drawable.ic_baseline_cast_connected_24); + } else { + castItem.setIcon(R.drawable.ic_baseline_cast_24); + } } + return true; } @Override @@ -630,6 +727,101 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } finish(); return true; + } else if (item.getItemId() == R.id.action_cast) { + if (chromeCasts != null && chromeCasts.size() > 0) { + String[] chromecast_choice = new String[chromeCasts.size()]; + AlertDialog.Builder alt_bld = new AlertDialog.Builder(this); + alt_bld.setTitle(R.string.chromecast_choice); + int i = 0; + for (ChromeCast cc : chromeCasts) { + chromecast_choice[i] = cc.getTitle(); + i++; + } + i = 0; + for (ChromeCast cc : chromeCasts) { + if (chromecastActivated && cc.isConnected()) { + break; + } + i++; + } + + alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> { + chromeCast = chromeCasts.get(position); + new Thread(() -> { + if (chromeCast != null) { + Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); + Bundle b = new Bundle(); + if (chromecastActivated) { + b.putInt("displayed", 0); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(PeertubeActivity.this).sendBroadcast(intentBC); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + binding.doubleTapPlayerView.setVisibility(View.VISIBLE); + binding.castController.setVisibility(View.GONE); + }; + mainHandler.post(myRunnable); + + } else { + b.putInt("displayed", 1); + b.putParcelable("castedTube", peertube); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(PeertubeActivity.this).sendBroadcast(intentBC); + try { + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + invalidateOptionsMenu(); + binding.castLoader.setVisibility(View.VISIBLE); + player.setPlayWhenReady(false); + binding.doubleTapPlayerView.setVisibility(View.GONE); + binding.castController.setVisibility(View.VISIBLE); + dialog.dismiss(); + if (chromeCastVideoURL != null) { + if (player != null && player.getCurrentPosition() > 0) { + chromeCastVideoURL += "?start=" + (player.getCurrentPosition() / 1000); + } + } + }; + mainHandler.post(myRunnable); + if (!chromeCast.isConnected()) { + chromeCast.connect(); + } + myRunnable = this::invalidateOptionsMenu; + mainHandler.post(myRunnable); + Status status = chromeCast.getStatus(); + if (chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) { + chromeCast.launchApp(CAST_ID); + } + + if (chromeCastVideoURL != null) { + String mime = MimeTypeMap.getFileExtensionFromUrl(chromeCastVideoURL); + chromeCast.setRequestTimeout(60000); + chromeCast.load(peertube.getTitle(), null, chromeCastVideoURL, mime); + chromeCast.play(); + binding.castPlay.setImageResource(R.drawable.ic_baseline_pause_32); + } + myRunnable = () -> binding.castLoader.setVisibility(View.GONE); + mainHandler.post(myRunnable); + } catch (IOException | GeneralSecurityException e) { + e.printStackTrace(); + } + } + + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + invalidateOptionsMenu(); + dialog.dismiss(); + }; + mainHandler.post(myRunnable); + + } + }).start(); + + }); + alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss()); + AlertDialog alert = alt_bld.create(); + alert.show(); + } } return super.onOptionsItemSelected(item); } @@ -671,13 +863,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd alertDialog2.show(); } - public FullScreenMediaController.fullscreen getFullscreen() { - return fullscreen; - } - - public void setFullscreen(FullScreenMediaController.fullscreen fullscreen) { - this.fullscreen = fullscreen; - } public void manageCaptions(APIResponse apiResponse) { if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getCaptions() == null || apiResponse.getCaptions().size() == 0) { @@ -710,12 +895,64 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd i++; } nextVideo = apiResponse.getPeertubes().get(i); - MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.parse(nextVideo.getFileUrl(null, PeertubeActivity.this))).build(); - player.addMediaItem(mediaItem); + if (!playedVideos.contains(nextVideo.getId()) && player != null && nextVideo.getFileUrl(null, PeertubeActivity.this) != null) { + MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.parse(nextVideo.getFileUrl(null, PeertubeActivity.this))).build(); + player.addMediaItem(mediaItem); + } } - @SuppressLint("ClickableViewAccessibility") + public void manageVIewVideo(APIResponse apiResponse) { + if (!isRemote && apiResponse != null && apiResponse.getPeertubes() != null && apiResponse.getPeertubes().get(0).getErrorCode() == 1 && apiResponse.getPeertubes().get(0).getOriginUrl() != null) { + String url = apiResponse.getPeertubes().get(0).getOriginUrl(); + Pattern link = Pattern.compile("(https?://[\\da-z.-]+\\.[a-z.]{2,10})/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})(\\?start=(\\d+[hH])?(\\d+[mM])?(\\d+[sS])?)?$"); + Matcher matcherLink = link.matcher(url); + if (matcherLink.find()) { + String instance = matcherLink.group(1); + String uuid = matcherLink.group(2); + String hour = matcherLink.group(4); + String min = matcherLink.group(5); + String sec = matcherLink.group(6); + int hourInt, minInt, secInt; + int totalSeconds = 0; + if (hour != null) { + hourInt = Integer.parseInt(hour.replace("h", "")); + totalSeconds += 3600 * hourInt; + } + if (min != null) { + minInt = Integer.parseInt(min.replace("m", "")); + totalSeconds += 60 * minInt; + } + if (sec != null) { + secInt = Integer.parseInt(sec.replace("strue", "")); + totalSeconds += secInt; + } + + if (instance != null && uuid != null) { + peertubeInstance = instance.replace("https://", "").replace("http://", ""); + sepiaSearch = true; // Sepia search flag is used because, at this time we don't know if the video is federated. + videoUuid = uuid; + peertube = new VideoData.Video(); + peertube.setUuid(uuid); + peertube.setEmbedUrl(url); + isRemote = true; + if (totalSeconds > 0) { + VideoData.UserHistory userHistory = new VideoData.UserHistory(); + userHistory.setCurrentTime(totalSeconds); + peertube.setUserHistory(userHistory); + } + TimelineVM viewModelTimeline = new ViewModelProvider(PeertubeActivity.this).get(TimelineVM.class); + viewModelTimeline.getVideo(peertubeInstance, peertube.getUuid(), false).observe(PeertubeActivity.this, this::manageVIewVideo); + } + } + return; + } + + if (apiResponse != null && apiResponse.getPeertubes() != null && apiResponse.getPeertubes().size() > 0 && apiResponse.getPeertubes().get(0).getErrorMessage() != null) { + Toasty.error(PeertubeActivity.this, apiResponse.getPeertubes().get(0).getErrorMessage(), Toast.LENGTH_LONG).show(); + binding.loader.setVisibility(View.GONE); + return; + } if (apiResponse == null || (apiResponse.getError() != null) || apiResponse.getPeertubes() == null || apiResponse.getPeertubes().size() == 0) { Toasty.error(PeertubeActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); binding.loader.setVisibility(View.GONE); @@ -727,11 +964,19 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd return; } long position = -1; + + long previousPositionHistory = 0; + if (peertube != null && peertube.getUserHistory() != null) { + previousPositionHistory = peertube.getUserHistory().getCurrentTime(); + } + peertube = apiResponse.getPeertubes().get(0); + VideoData.UserHistory userHistory = new VideoData.UserHistory(); + userHistory.setCurrentTime(previousPositionHistory); + peertube.setUserHistory(userHistory); + if (peertube.getUserHistory() != null) { position = peertube.getUserHistory().getCurrentTime() * 1000; } - - peertube = apiResponse.getPeertubes().get(0); if (peertube.getTags() != null && peertube.getTags().size() > 0) { SearchVM searchViewModel = new ViewModelProvider(PeertubeActivity.this).get(SearchVM.class); searchViewModel.searchNextVideos(peertube.getTags()).observe(PeertubeActivity.this, this::manageNextVideos); @@ -748,31 +993,87 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.writeCommentContainer.setVisibility(View.GONE); } + if (peertube.isNsfw()) { + binding.videoSensitive.setVisibility(View.VISIBLE); + } else { + binding.videoSensitive.setVisibility(View.INVISIBLE); + } + binding.peertubePlaylist.setOnClickListener(v -> { PlaylistsVM viewModelOwnerPlaylist = new ViewModelProvider(PeertubeActivity.this).get(PlaylistsVM.class); viewModelOwnerPlaylist.manage(PlaylistsVM.action.GET_PLAYLISTS, null, null).observe(PeertubeActivity.this, this::manageVIewPlaylists); }); - if (peertube.isCommentsEnabled()) { - if (Helper.isLoggedIn(PeertubeActivity.this)) { - binding.postCommentButton.setVisibility(View.VISIBLE); - } else { - binding.postCommentButton.setVisibility(View.GONE); + binding.videoInformation.setOnClickListener(v -> { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this); + LayoutInflater inflater = getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.popup_video_info, new LinearLayout(PeertubeActivity.this), false); + TextView info_privacy = dialogView.findViewById(R.id.info_privacy); + TextView info_published_at = dialogView.findViewById(R.id.info_published_at); + TextView info_category = dialogView.findViewById(R.id.info_category); + TextView info_license = dialogView.findViewById(R.id.info_license); + TextView info_language = dialogView.findViewById(R.id.info_language); + TextView info_duration = dialogView.findViewById(R.id.info_duration); + TextView info_tags = dialogView.findViewById(R.id.info_tags); + + LinkedHashMap privaciesInit = new LinkedHashMap<>(peertubeInformation.getPrivacies()); + info_privacy.setText(privaciesInit.get(peertube.getPrivacy().getId())); + LinkedHashMap licenseInit = new LinkedHashMap<>(peertubeInformation.getLicences()); + info_license.setText(licenseInit.get(peertube.getLicence().getId())); + LinkedHashMap languageStr = new LinkedHashMap<>(peertubeInformation.getLanguages()); + info_language.setText(languageStr.get(peertube.getLanguage().getId())); + LinkedHashMap categoryInit = new LinkedHashMap<>(peertubeInformation.getCategories()); + info_category.setText(categoryInit.get(peertube.getCategory().getId())); + + info_duration.setText(Helper.secondsToString(peertube.getDuration())); + String format = DateFormat.getDateInstance(DateFormat.LONG).format(peertube.getPublishedAt()); + info_published_at.setText(format); + List tags = peertube.getTags(); + StringBuilder sb = new StringBuilder(); + for (String tag : tags) { + sb.append("#").append(tag).append(" "); } - CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); - commentViewModel.getThread(sepiaSearch ? peertubeInstance : null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment); - if (Helper.isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { - binding.writeCommentContainer.setVisibility(View.VISIBLE); + + SpannableString spannableString = new SpannableString(sb.toString()); + for (String tag : tags) { + String target = "#" + tag; + if (spannableString.toString().contains(target)) { + for (int startPosition = -1; (startPosition = spannableString.toString().indexOf(target, startPosition + 1)) != -1; startPosition++) { + final int endPosition = startPosition + target.length(); + if (endPosition <= spannableString.toString().length() && endPosition >= startPosition) { + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + Intent intent = new Intent(PeertubeActivity.this, SearchActivity.class); + Bundle b = new Bundle(); + String search = tag.trim(); + b.putString("search", search); + intent.putExtras(b); + startActivity(intent); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(getResources().getColor(R.color.colorAccent)); + } + }, + startPosition, endPosition, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } } - binding.peertubeComments.setVisibility(View.VISIBLE); - } else { - binding.postCommentButton.setVisibility(View.GONE); - binding.peertubeComments.setVisibility(View.GONE); - binding.writeCommentContainer.setVisibility(View.GONE); - binding.noActionText.setText(getString(R.string.comment_no_allowed_peertube)); - binding.noAction.setVisibility(View.VISIBLE); - binding.writeCommentContainer.setVisibility(View.GONE); - } + info_tags.setText(spannableString, TextView.BufferType.SPANNABLE); + info_tags.setMovementMethod(LinkMovementMethod.getInstance()); + dialogBuilder.setView(dialogView); + dialogBuilder.setNeutralButton(R.string.close, (dialog, id) -> dialog.dismiss()); + AlertDialog alertDialog = dialogBuilder.create(); + alertDialog.show(); + }); + + fetchComments(); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); setTitle(peertube.getName()); @@ -783,7 +1084,13 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.peertubeDislikeCount.setText(Helper.withSuffix(peertube.getDislikes())); binding.peertubeLikeCount.setText(Helper.withSuffix(peertube.getLikes())); binding.peertubeViewCount.setText(Helper.withSuffix(peertube.getViews())); - loadGiF(PeertubeActivity.this, peertube.getChannel().getAvatar() != null ? peertube.getChannel().getAvatar().getPath() : null, binding.ppChannel); + String ppChannelURL; + if (sepiaSearch) { + ppChannelURL = peertube.getChannel().getAvatar() != null ? "https://" + peertubeInstance + peertube.getChannel().getAvatar().getPath() : null; + } else { + ppChannelURL = peertube.getChannel().getAvatar() != null ? peertube.getChannel().getAvatar().getPath() : null; + } + loadGiF(PeertubeActivity.this, ppChannelURL, binding.ppChannel); binding.ppChannel.setOnClickListener(v -> { Intent intent = new Intent(PeertubeActivity.this, ShowChannelActivity.class); Bundle b = new Bundle(); @@ -853,7 +1160,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd }); - if (mode == Helper.VIDEO_MODE_NORMAL) { + if (mode != Helper.VIDEO_MODE_WEBVIEW) { player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build(); player.addVideoListener(PeertubeActivity.this); @@ -864,9 +1171,12 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd startStream( apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this), apiResponse.getPeertubes().get(0).getStreamingPlaylists().size() > 0 ? apiResponse.getPeertubes().get(0).getStreamingPlaylists().get(0).getPlaylistUrl() : null, - autoPlay, position, null, null); + autoPlay, position, null, null, true); player.prepare(); player.setPlayWhenReady(autoPlay); + if (autoPlay) { + binding.doubleTapPlayerView.hideController(); + } } @@ -875,9 +1185,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd popup.getMenuInflater() .inflate(R.menu.main_video, popup.getMenu()); - if (captions == null) { - popup.getMenu().findItem(R.id.action_captions).setEnabled(false); - } if (!isMyVideo) { popup.getMenu().findItem(R.id.action_edit).setVisible(false); } @@ -931,63 +1238,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd b.putString("video_id", peertube.getUuid()); intent.putExtras(b); startActivity(intent); - } else if (itemId == R.id.action_captions) { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this); - if (captions == null) { - return true; - } - - String[] itemsKeyLanguage = new String[captions.size() + 1]; - String[] itemsLabelLanguage = new String[captions.size() + 1]; - itemsLabelLanguage[0] = getString(R.string.none); - itemsKeyLanguage[0] = "null"; - int i = 1; - if (captions.size() > 0) { - for (Caption caption : captions) { - ItemStr lang = caption.getLanguage(); - itemsLabelLanguage[i] = lang.getLabel(); - itemsKeyLanguage[i] = lang.getId(); - i++; - } - } - dialogBuilder.setSingleChoiceItems(itemsLabelLanguage, i, (dialog, which) -> { - - Uri uri = null; - if (which > 0) { - if (!sepiaSearch) { - uri = Uri.parse("https://" + getLiveInstance(PeertubeActivity.this) + captions.get(which - 1).getCaptionPath()); - } else { - uri = Uri.parse("https://" + peertubeInstance + captions.get(which - 1).getCaptionPath()); - } - } - long newPosition = player.getCurrentPosition(); - - if (player != null) - player.release(); - - TrackSelector trackSelector = new DefaultTrackSelector(PeertubeActivity.this, new AdaptiveTrackSelection.Factory()); - player = new SimpleExoPlayer.Builder(PeertubeActivity.this).setTrackSelector(trackSelector).build(); - binding.mediaVideo.player(player); - binding.doubleTapPlayerView.setPlayer(player); - startStream( - apiResponse.getPeertubes().get(0).getFileUrl(null, PeertubeActivity.this), - null, - true, - newPosition, - uri, - itemsKeyLanguage[which] - ); - dialog.dismiss(); - }); - - dialogBuilder.setOnDismissListener(dialogInterface -> { - - }); - dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); - - AlertDialog alertDialog = dialogBuilder.create(); - alertDialog.setTitle(getString(R.string.pickup_captions)); - alertDialog.show(); } else if (itemId == R.id.action_report) { AlertDialog alertDialog; AlertDialog.Builder dialogBuilder; @@ -1008,11 +1258,10 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd }); } - private void startStream(String videoURL, String streamingPlaylistsURLS, boolean autoPlay, long position, Uri subtitles, String lang) { - if (videoURL != null) { + private void stream(String videoURL, String streamingPlaylistsURLS, boolean autoPlay, long position, Uri subtitles, String lang) { + if (videoURL != null && !videoURL.endsWith("m3u8")) { if (videoURL.endsWith(".torrent")) { - torrentStream.startStream(videoURL); return; } else { @@ -1049,6 +1298,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } } } else { + if (streamingPlaylistsURLS == null && videoURL != null) { + streamingPlaylistsURLS = videoURL; + } MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.parse(streamingPlaylistsURLS)).build(); HlsMediaSource hlsMediaSource = new HlsMediaSource.Factory(new DefaultHttpDataSourceFactory(System.getProperty("http.agent"))) .createMediaSource(mediaItem); @@ -1059,17 +1311,74 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd player.seekTo(0, position); } player.setPlayWhenReady(autoPlay); + if (autoPlay) { + binding.doubleTapPlayerView.hideController(); + } + } + + private void fetchComments() { + if (peertube.isCommentsEnabled()) { + if (Helper.isLoggedIn(PeertubeActivity.this)) { + binding.postCommentButton.setVisibility(View.VISIBLE); + } else { + binding.postCommentButton.setVisibility(View.GONE); + } + CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); + commentViewModel.getThread(sepiaSearch ? peertubeInstance : null, videoUuid, max_id).observe(PeertubeActivity.this, this::manageVIewComment); + if (Helper.isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { + binding.writeCommentContainer.setVisibility(View.VISIBLE); + } + binding.peertubeComments.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.GONE); + } else { + binding.postCommentButton.setVisibility(View.GONE); + binding.peertubeComments.setVisibility(View.GONE); + binding.writeCommentContainer.setVisibility(View.GONE); + binding.noActionText.setText(getString(R.string.comment_no_allowed_peertube)); + binding.noAction.setVisibility(View.VISIBLE); + binding.writeCommentContainer.setVisibility(View.GONE); + } + } + + private void startStream(String videoURL, String streamingPlaylistsURLS, boolean autoPlay, long position, Uri subtitles, String lang, boolean promptNSFW) { + + chromeCastVideoURL = videoURL; + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + String nsfwAction = sharedpreferences.getString(getString(R.string.set_video_sensitive_choice), Helper.BLUR); + if (promptNSFW && peertube != null && peertube.isNsfw() && (nsfwAction.compareTo(Helper.BLUR) == 0 || nsfwAction.compareTo(Helper.DO_NOT_LIST) == 0)) { + AlertDialog alertDialog; + AlertDialog.Builder dialogBuilder; + dialogBuilder = new AlertDialog.Builder(PeertubeActivity.this); + dialogBuilder.setTitle(R.string.nsfw_title_warning); + dialogBuilder.setCancelable(false); + dialogBuilder.setMessage(R.string.nsfw_message_warning); + dialogBuilder.setNegativeButton(R.string.no, (dialog, id) -> { + dialog.dismiss(); + finish(); + }); + dialogBuilder.setPositiveButton(R.string.play, (dialog, id) -> { + stream(videoURL, streamingPlaylistsURLS, autoPlay, position, subtitles, lang); + dialog.dismiss(); + }); + alertDialog = dialogBuilder.create(); + alertDialog.show(); + } else { + stream(videoURL, streamingPlaylistsURLS, autoPlay, position, subtitles, lang); + } + + } @Override public void onConfigurationChanged(@NotNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - + if (binding.castController.getVisibility() == View.VISIBLE) { + return; + } if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { if (mode != Helper.VIDEO_MODE_WEBVIEW) { openFullscreenDialog(); } - setFullscreen(FullScreenMediaController.fullscreen.ON); if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) { LinearLayout.LayoutParams param = new LinearLayout.LayoutParams( ConstraintLayout.LayoutParams.MATCH_PARENT, @@ -1082,7 +1391,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd if (mode != Helper.VIDEO_MODE_WEBVIEW) { closeFullscreenDialog(); } - setFullscreen(FullScreenMediaController.fullscreen.OFF); if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) { LinearLayout.LayoutParams param = new LinearLayout.LayoutParams( ConstraintLayout.LayoutParams.MATCH_PARENT, @@ -1092,7 +1400,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.videoContainer.setLayoutParams(param); } } - change(); } @Override @@ -1106,9 +1413,6 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd torrentStream.stopStream(); } unregisterReceiver(); - if (fullScreenDialog != null && fullScreenDialog.isShowing()) { - fullScreenDialog.dismiss(); - } } @Override @@ -1123,6 +1427,9 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd onStopCalled = false; if (player != null && !player.isPlaying()) { player.setPlayWhenReady(autoPlay); + if (autoPlay) { + binding.doubleTapPlayerView.hideController(); + } } } @@ -1134,7 +1441,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } if (player != null && (!isPlayInMinimized || !playInMinimized)) { player.setPlayWhenReady(false); - } else if (playInMinimized) { + } else if (playInMinimized && binding.castController.getVisibility() != View.VISIBLE) { enterVideoMode(); } } @@ -1152,6 +1459,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd if (player != null && isPlayInMinimized) { if (!sharedpreferences.getBoolean(getString(R.string.set_play_screen_lock_choice), false)) { player.setPlayWhenReady(false); + } else { + player.setWakeMode(C.WAKE_MODE_NETWORK); } } } @@ -1161,15 +1470,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } private void unregisterReceiver() { - int apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= 7) { - try { - getApplicationContext().unregisterReceiver(mPowerKeyReceiver); - } catch (IllegalArgumentException e) { - mPowerKeyReceiver = null; - } - } else { + if (mPowerKeyReceiver != null) { getApplicationContext().unregisterReceiver(mPowerKeyReceiver); mPowerKeyReceiver = null; } @@ -1184,6 +1485,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd private void enterVideoMode() { if (playInMinimized && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && player != null) { isPlayInMinimized = true; + setRequestedOrientationCustom(initialOrientation); MediaSessionCompat mediaSession = new MediaSessionCompat(this, getPackageName()); MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession); mediaSessionConnector.setPlayer(player); @@ -1198,25 +1500,39 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd @Override public void onBackPressed() { + + if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE) { + closeSubMenuMenuOptions(); + return; + } + if (binding.videoParams.getVisibility() == View.VISIBLE) { + closeMainMenuOptions(); + return; + } if (binding.postComment.getVisibility() == View.VISIBLE) { closePostComment(); - } else if (binding.replyThread.getVisibility() == View.VISIBLE) { + return; + } + if (binding.replyThread.getVisibility() == View.VISIBLE) { closeCommentThread(); + return; + } + + if (fullScreenMode && player != null && player.isPlaying()) { + player.setPlayWhenReady(false); + return; + } + + if (playInMinimized && player != null) { + enterVideoMode(); } else { - if (playInMinimized && player != null) { - enterVideoMode(); - } else { - super.onBackPressed(); - } + super.onBackPressed(); } } @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) { - if (isInPictureInPictureMode) { - setFullscreen(FullScreenMediaController.fullscreen.ON); - } else { - setFullscreen(FullScreenMediaController.fullscreen.OFF); + if (!isInPictureInPictureMode) { if (onStopCalled) { isPlayInMinimized = false; finishAndRemoveTask(); @@ -1224,80 +1540,46 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } } - public void displayResolution() { - AlertDialog.Builder builderSingle = new AlertDialog.Builder(PeertubeActivity.this); - builderSingle.setTitle(R.string.pickup_resolution); - final ArrayAdapter arrayAdapter = new ArrayAdapter<>(PeertubeActivity.this, android.R.layout.select_dialog_item); - for (File file : peertube.getFiles()) { - if (file.getResolutions() != null) { - if (file.getResolutions().getLabel().compareTo("0p") != 0) { - arrayAdapter.add(file.getResolutions().getLabel()); + + private void toogleFullscreen(boolean fullscreen) { + + if (fullscreen) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + Objects.requireNonNull(getSupportActionBar()).hide(); + binding.bottomVideo.setVisibility(View.GONE); + Objects.requireNonNull(getSupportActionBar()).hide(); + if (videoOrientationType == videoOrientation.LANDSCAPE) { + if (getResources().getConfiguration().orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + setRequestedOrientationCustom(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } } + } else { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); + Objects.requireNonNull(getSupportActionBar()).show(); + binding.bottomVideo.setVisibility(View.VISIBLE); + Objects.requireNonNull(getSupportActionBar()).show(); } - builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); - builderSingle.setAdapter(arrayAdapter, (dialog, which) -> { - String res = Objects.requireNonNull(arrayAdapter.getItem(which)).substring(0, Objects.requireNonNull(arrayAdapter.getItem(which)).length() - 1); - - binding.loader.setVisibility(View.VISIBLE); - long position = player.getCurrentPosition(); - PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller); - TextView resolution = controlView.findViewById(R.id.resolution); - resolution.setText(String.format("%sp", res)); - if (mode == Helper.VIDEO_MODE_NORMAL) { - if (player != null) - player.release(); - player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build(); - binding.mediaVideo.player(player); - binding.doubleTapPlayerView.setPlayer(player); - binding.loader.setVisibility(View.GONE); - startStream( - peertube.getFileUrl(res, PeertubeActivity.this), - peertube.getStreamingPlaylists().size() > 0 ? peertube.getStreamingPlaylists().get(0).getPlaylistUrl() : null, - true, position, null, null); - } - - }); - builderSingle.show(); - } - - private void initFullscreenDialog() { - - fullScreenDialog = new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen) { - public void onBackPressed() { - if (player != null && player.isPlaying() && fullScreenMode) { - player.setPlayWhenReady(false); - } - if (fullScreenMode) { - closeFullscreenDialog(); - if (!Helper.isTablet(PeertubeActivity.this) && initialOrientation != Configuration.ORIENTATION_LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - Handler handler = new Handler(); - handler.postDelayed(() -> setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR), 2000); - } else { - binding.peertubeInformationContainer.setVisibility(View.VISIBLE); - } - } - super.onBackPressed(); - } - }; } private void openFullscreenDialog() { - - ((ViewGroup) binding.doubleTapPlayerView.getParent()).removeView(binding.doubleTapPlayerView); - fullScreenDialog.addContentView(binding.doubleTapPlayerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_exit_24)); fullScreenMode = true; + toogleFullscreen(true); + } - fullScreenDialog.show(); + private void closeFullscreenDialog() { + fullScreenMode = false; + fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_24)); + toogleFullscreen(false); } public void openCommentThread(Comment comment) { CommentVM commentViewModel = new ViewModelProvider(PeertubeActivity.this).get(CommentVM.class); binding.peertubeReply.setVisibility(View.GONE); - commentViewModel.getRepliesComment(videoUuid, comment.getId()).observe(PeertubeActivity.this, apiResponse -> manageVIewCommentReply(comment, apiResponse)); + commentViewModel.getRepliesComment(sepiaSearch ? peertubeInstance : null, videoUuid, comment.getId()).observe(PeertubeActivity.this, apiResponse -> manageVIewCommentReply(comment, apiResponse)); binding.replyThread.setVisibility(View.VISIBLE); TranslateAnimation animate = new TranslateAnimation( @@ -1313,6 +1595,7 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd public void onAnimationEnd(Animation animation) { binding.peertubeInformationContainer.setVisibility(View.GONE); } + @Override public void onAnimationRepeat(Animation animation) { } @@ -1321,6 +1604,273 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd binding.replyThread.startAnimation(animate); } + + public void openMainMenuOptions() { + binding.videoParams.setVisibility(View.VISIBLE); + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + int height = displayMetrics.heightPixels; + binding.doubleTapPlayerView.hideController(); + List menuItemVideos = new ArrayList<>(); + if (peertube.getAllFile(PeertubeActivity.this) != null && peertube.getAllFile(PeertubeActivity.this).size() > 0) { + MenuItemVideo resolutionItem = new MenuItemVideo(); + resolutionItem.setIcon(R.drawable.ic_baseline_high_quality_24); + resolutionItem.setTitle(getString(R.string.pickup_resolution)); + resolutionItem.setAction(MenuItemVideo.actionType.RESOLUTION); + menuItemVideos.add(resolutionItem); + + } + MenuItemVideo speedItem = new MenuItemVideo(); + speedItem.setIcon(R.drawable.ic_baseline_speed_24); + speedItem.setTitle(getString(R.string.playback_speed)); + speedItem.setAction(MenuItemVideo.actionType.SPEED); + menuItemVideos.add(speedItem); + + if (captions != null) { + MenuItemVideo captionItem = new MenuItemVideo(); + captionItem.setIcon(R.drawable.ic_baseline_subtitles_24); + captionItem.setTitle(getString(R.string.captions)); + captionItem.setAction(MenuItemVideo.actionType.CAPTION); + menuItemVideos.add(captionItem); + } + + MenuItemVideo autoNextItem = new MenuItemVideo(); + autoNextItem.setIcon(R.drawable.ic_baseline_play_arrow_24); + autoNextItem.setTitle(getString(R.string.set_autoplay_next_video_settings)); + autoNextItem.setAction(MenuItemVideo.actionType.AUTONEXT); + menuItemVideos.add(autoNextItem); + + MenuAdapter menuAdapter = new MenuAdapter(menuItemVideos); + binding.mainOptionsVideo.setAdapter(menuAdapter); + menuAdapter.itemClicked = this; + binding.mainOptionsVideo.setLayoutManager(new LinearLayoutManager(PeertubeActivity.this)); + + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + height, + 0); + animate.setDuration(500); + binding.videoParams.startAnimation(animate); + } + + @Override + public void onItemClicked(MenuItemVideo.actionType action) { + binding.videoParamsSubmenu.setVisibility(View.VISIBLE); + List items = new ArrayList<>(); + switch (action) { + case RESOLUTION: + binding.subMenuTitle.setText(R.string.pickup_resolution); + int position = 0; + for (File file : peertube.getFiles()) { + if (file.getResolutions() != null) { + if (file.getResolutions().getLabel().compareTo("0p") != 0) { + MenuItemView item = new MenuItemView(); + item.setId(position); + item.setLabel(file.getResolutions().getLabel()); + if (file.getResolutions().getLabel().compareTo(currentResolution) == 0) { + item.setSelected(true); + } + items.add(item); + position++; + } + } + } + break; + case SPEED: + binding.subMenuTitle.setText(R.string.playback_speed); + items = new ArrayList<>(); + items.add(new MenuItemView(25, "0.25x", player.getPlaybackParameters().speed == 0.25)); + items.add(new MenuItemView(50, "0.5x", player.getPlaybackParameters().speed == 0.5)); + items.add(new MenuItemView(75, "0.75x", player.getPlaybackParameters().speed == 0.75)); + items.add(new MenuItemView(100, getString(R.string.normal), player.getPlaybackParameters().speed == 1)); + items.add(new MenuItemView(125, "1.25x", player.getPlaybackParameters().speed == 1.25)); + items.add(new MenuItemView(150, "1.5x", player.getPlaybackParameters().speed == 1.5)); + items.add(new MenuItemView(175, "1.75x", player.getPlaybackParameters().speed == 1.75)); + items.add(new MenuItemView(200, "2x", player.getPlaybackParameters().speed == 2.0)); + break; + case CAPTION: + binding.subMenuTitle.setText(R.string.pickup_captions); + items = new ArrayList<>(); + items.add(new MenuItemView(-1, "null", getString(R.string.none), currentCaption.compareTo("null") == 0)); + int i = 0; + for (Caption caption : captions) { + items.add(new MenuItemView(i, caption.getLanguage().getId(), caption.getLanguage().getLabel(), currentCaption.compareTo(caption.getLanguage().getId()) == 0)); + } + break; + case AUTONEXT: + binding.subMenuTitle.setText(R.string.set_autoplay_next_video_settings); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + boolean autoplayNextVideo = sharedpreferences.getBoolean(getString(R.string.set_autoplay_next_video_choice), true); + items.add(new MenuItemView(0, getString(R.string.no), !autoplayNextVideo)); + items.add(new MenuItemView(1, getString(R.string.yes), autoplayNextVideo)); + break; + } + MenuItemAdapter menuItemAdapter = new MenuItemAdapter(action, items); + binding.subMenuRecycler.setAdapter(menuItemAdapter); + menuItemAdapter.itemAction = this; + binding.subMenuRecycler.setLayoutManager(new LinearLayoutManager(PeertubeActivity.this)); + + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + int height = displayMetrics.heightPixels; + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + height, + 0); + animate.setDuration(500); + binding.videoParamsSubmenu.startAnimation(animate); + } + + @Override + public void which(MenuItemVideo.actionType action, MenuItemView item) { + closeMainMenuOptions(); + switch (action) { + case RESOLUTION: + String res = item.getLabel(); + binding.loader.setVisibility(View.VISIBLE); + long position = player.getCurrentPosition(); + PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller); + TextView resolution = controlView.findViewById(R.id.resolution); + currentResolution = res; + resolution.setText(String.format("%s", res)); + if (mode == Helper.VIDEO_MODE_NORMAL) { + if (player != null) + player.release(); + player = new SimpleExoPlayer.Builder(PeertubeActivity.this).build(); + binding.mediaVideo.player(player); + binding.doubleTapPlayerView.setPlayer(player); + binding.loader.setVisibility(View.GONE); + startStream( + peertube.getFileUrl(res, PeertubeActivity.this), + peertube.getStreamingPlaylists().size() > 0 ? peertube.getStreamingPlaylists().get(0).getPlaylistUrl() : null, + true, position, null, null, false); + } + break; + case SPEED: + int speed = item.getId(); + float ratio = (float) speed / 100; + PlaybackParameters param = new PlaybackParameters(ratio); + if (player != null) { + player.setPlaybackParameters(param); + } + break; + case CAPTION: + Uri uri = null; + Caption captionToUse = null; + for (Caption caption : captions) { + if (caption.getLanguage().getId().compareTo(item.getStrId()) == 0) { + captionToUse = caption; + break; + } + } + if (captionToUse != null) { + if (!sepiaSearch) { + uri = Uri.parse("https://" + HelperInstance.getLiveInstance(PeertubeActivity.this) + captionToUse.getCaptionPath()); + } else { + uri = Uri.parse("https://" + peertubeInstance + captionToUse.getCaptionPath()); + } + } + currentCaption = item.getStrId(); + long newPosition = player.getCurrentPosition(); + + if (player != null) + player.release(); + + TrackSelector trackSelector = new DefaultTrackSelector(PeertubeActivity.this, new AdaptiveTrackSelection.Factory()); + player = new SimpleExoPlayer.Builder(PeertubeActivity.this).setTrackSelector(trackSelector).build(); + binding.mediaVideo.player(player); + binding.doubleTapPlayerView.setPlayer(player); + startStream( + peertube.getFileUrl(null, PeertubeActivity.this), + null, + true, + newPosition, + uri, + item.getStrId(), + false + ); + break; + case AUTONEXT: + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putBoolean(getString(R.string.set_autoplay_next_video_choice), item.getId() == 1); + editor.apply(); + if (Helper.isLoggedIn(PeertubeActivity.this)) { + new Thread(() -> { + UserSettings userSettings = new UserSettings(); + userSettings.setAutoPlayNextVideo(item.getId() == 1); + try { + RetrofitPeertubeAPI api = new RetrofitPeertubeAPI(PeertubeActivity.this); + api.updateUser(userSettings); + } catch (Exception | Error e) { + e.printStackTrace(); + } + }).start(); + } + break; + } + closeSubMenuMenuOptions(); + } + + public void closeMainMenuOptions() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + int height = displayMetrics.heightPixels; + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + 0, + height); + animate.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + binding.videoParams.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + animate.setDuration(500); + binding.videoParams.startAnimation(animate); + } + + + + public void closeSubMenuMenuOptions() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + int height = displayMetrics.heightPixels; + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + 0, + height); + animate.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + binding.videoParamsSubmenu.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + animate.setDuration(500); + binding.videoParamsSubmenu.startAnimation(animate); + } + + private void sendComment(Comment comment, int position) { if (isLoggedIn(PeertubeActivity.this) && !sepiaSearch) { if (comment == null) { @@ -1480,16 +2030,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } } - private void closeFullscreenDialog() { - ((ViewGroup) binding.doubleTapPlayerView.getParent()).removeView(binding.doubleTapPlayerView); - ((FrameLayout) findViewById(R.id.main_media_frame)).addView(binding.doubleTapPlayerView); - fullScreenMode = false; - fullScreenDialog.dismiss(); - fullScreenIcon.setImageDrawable(ContextCompat.getDrawable(PeertubeActivity.this, R.drawable.ic_baseline_fullscreen_24)); - } - - private void initFullscreenButton() { + private void initControllerButtons() { PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller); fullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon); @@ -1497,41 +2039,48 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd fullScreenButton.setOnClickListener(v -> { if (!fullScreenMode) { openFullscreenDialog(); - if (videoOrientationType == videoOrientation.LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } } else { closeFullscreenDialog(); - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - Handler handler = new Handler(); - handler.postDelayed(() -> setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR), 2000); - + setRequestedOrientationCustom(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } }); + ImageButton playButton = controlView.findViewById(R.id.exo_play); playButton.setOnClickListener(v -> { if (autoFullscreen && !fullScreenMode) { openFullscreenDialog(); - if (videoOrientationType == videoOrientation.LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } } player.setPlayWhenReady(true); }); View exo_next = controlView.findViewById(R.id.exo_next); exo_next.setOnClickListener(v -> playNextVideo()); + + View exoSettings = controlView.findViewById(R.id.exo_settings); + exoSettings.setOnClickListener(v -> { + if (binding.videoParams.getVisibility() == View.VISIBLE) { + closeMainMenuOptions(); + } else { + openMainMenuOptions(); + } + }); + } + + private void setRequestedOrientationCustom(int orientationCustom) { + setRequestedOrientation(orientationCustom); + Handler handler = new Handler(); + handler.postDelayed(() -> setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR), 2000); } private void initResolution() { PlayerControlView controlView = binding.doubleTapPlayerView.findViewById(R.id.exo_controller); TextView resolution = controlView.findViewById(R.id.resolution); - if (peertube.getFiles() != null && peertube.getFiles().size() > 0) { - resolution.setText(String.format("%s", Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()).getResolutions().getLabel())); - resolution.setOnClickListener(v -> displayResolution()); + if (Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()) != null) { + currentResolution = Helper.defaultFile(PeertubeActivity.this, peertube.getFiles()).getResolutions().getLabel(); + if (peertube.getFiles() != null && peertube.getFiles().size() > 0) { + resolution.setText(String.format("%s", currentResolution)); + } else { + resolution.setVisibility(View.GONE); + } } else { resolution.setVisibility(View.GONE); } @@ -1646,6 +2195,31 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + + if (MotionEvent.ACTION_UP == event.getAction()) { + Rect viewRectParams = new Rect(); + binding.videoParams.getGlobalVisibleRect(viewRectParams); + if (binding.videoParams.getVisibility() == View.VISIBLE && !viewRectParams.contains((int) event.getRawX(), (int) event.getRawY())) { + closeMainMenuOptions(); + if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE) { + closeSubMenuMenuOptions(); + } + } + Rect viewRectParamsSub = new Rect(); + binding.videoParamsSubmenu.getGlobalVisibleRect(viewRectParamsSub); + if (binding.videoParamsSubmenu.getVisibility() == View.VISIBLE && !viewRectParamsSub.contains((int) event.getRawX(), (int) event.getRawY())) { + closeSubMenuMenuOptions(); + if (binding.videoParams.getVisibility() == View.VISIBLE) { + closeMainMenuOptions(); + } + } + } + return super.dispatchTouchEvent(event); + } + private void updateHistory(long position) { SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); boolean storeInHistory = sharedpreferences.getBoolean(getString(R.string.set_store_in_history), true); @@ -1684,9 +2258,11 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd public void onMediaItemTransition(MediaItem mediaItem, int reason) { SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); boolean autoplayNextVideo = sharedpreferences.getBoolean(getString(R.string.set_autoplay_next_video_choice), true); - if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO && autoplayNextVideo) { + if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) { player.removeMediaItems(0, player.getMediaItemCount()); - playNextVideo(); + if (!sepiaSearch && autoplayNextVideo) { + playNextVideo(); + } } } @@ -1696,6 +2272,8 @@ public class PeertubeActivity extends AppCompatActivity implements CommentListAd } + + enum videoOrientation { LANDSCAPE, PORTRAIT diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java index ce8b7bc..b044650 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeEditUploadActivity.java @@ -16,21 +16,17 @@ package app.fedilab.fedilabtube; import android.Manifest; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.Spinner; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; @@ -40,39 +36,34 @@ import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; -import net.gotev.uploadservice.MultipartUploadRequest; -import net.gotev.uploadservice.ServerResponse; -import net.gotev.uploadservice.UploadInfo; -import net.gotev.uploadservice.UploadNotificationConfig; -import net.gotev.uploadservice.UploadStatusDelegate; - -import java.io.FileNotFoundException; -import java.net.MalformedURLException; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import app.fedilab.fedilabtube.client.APIResponse; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.ChannelData.Channel; +import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.data.VideoData.Video; import app.fedilab.fedilabtube.client.entities.Item; import app.fedilab.fedilabtube.client.entities.ItemStr; import app.fedilab.fedilabtube.client.entities.VideoParams; +import app.fedilab.fedilabtube.databinding.ActivityPeertubeEditBinding; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; import app.fedilab.fedilabtube.viewmodel.MyVideoVM; import app.fedilab.fedilabtube.viewmodel.PostActionsVM; import app.fedilab.fedilabtube.viewmodel.TimelineVM; import es.dmoral.toasty.Toasty; -import mabbas007.tagsedittext.TagsEditText; -import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.ActionType.PEERTUBEDELETEVIDEO; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.DataType.MY_CHANNELS; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; public class PeertubeEditUploadActivity extends AppCompatActivity { @@ -82,23 +73,21 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724; Item licenseToSend, privacyToSend, categoryToSend; ItemStr languageToSend; - private Button set_upload_submit; - private Spinner set_upload_privacy, set_upload_categories, set_upload_licenses, set_upload_languages, set_upload_channel; - private EditText p_video_title, p_video_description; - private TagsEditText p_video_tags; - private CheckBox set_upload_nsfw, set_upload_enable_comments; private LinkedHashMap channels; private String videoId; private Channel channel; - private ImageView p_video_preview; - private Button set_preview; private VideoParams videoParams; private Video video; private String channelToSendId; + private ActivityPeertubeEditBinding binding; + private Uri inputData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + binding = ActivityPeertubeEditBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); @@ -113,23 +102,8 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); - setContentView(R.layout.activity_peertube_edit); - set_upload_submit = findViewById(R.id.set_upload_submit); - Button set_upload_delete = findViewById(R.id.set_upload_delete); - set_upload_privacy = findViewById(R.id.set_upload_privacy); - set_upload_channel = findViewById(R.id.set_upload_channel); - set_upload_categories = findViewById(R.id.set_upload_categories); - set_upload_licenses = findViewById(R.id.set_upload_licenses); - set_upload_languages = findViewById(R.id.set_upload_languages); - p_video_title = findViewById(R.id.p_video_title); - p_video_description = findViewById(R.id.p_video_description); - p_video_tags = findViewById(R.id.p_video_tags); - p_video_preview = findViewById(R.id.p_video_preview); - set_upload_nsfw = findViewById(R.id.set_upload_nsfw); - set_upload_enable_comments = findViewById(R.id.set_upload_enable_comments); - set_preview = findViewById(R.id.set_preview); - set_upload_delete.setOnClickListener(v -> { + binding.setUploadDelete.setOnClickListener(v -> { AlertDialog.Builder builderInner; builderInner = new AlertDialog.Builder(PeertubeEditUploadActivity.this); builderInner.setMessage(getString(R.string.delete_video_confirmation)); @@ -165,7 +139,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } ArrayAdapter adapterCatgories = new ArrayAdapter<>(PeertubeEditUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, categoriesA); - set_upload_categories.setAdapter(adapterCatgories); + binding.setUploadCategories.setAdapter(adapterCatgories); //Populate licenses @@ -183,7 +157,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } ArrayAdapter adapterLicenses = new ArrayAdapter<>(PeertubeEditUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, licensesA); - set_upload_licenses.setAdapter(adapterLicenses); + binding.setUploadLicenses.setAdapter(adapterLicenses); //Populate languages @@ -201,7 +175,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } ArrayAdapter adapterLanguages = new ArrayAdapter<>(PeertubeEditUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, languagesA); - set_upload_languages.setAdapter(adapterLanguages); + binding.setUploadLanguages.setAdapter(adapterLanguages); //Populate languages @@ -220,18 +194,19 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeEditUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, privaciesA); - set_upload_privacy.setAdapter(adapterPrivacies); + binding.setUploadPrivacy.setAdapter(adapterPrivacies); TimelineVM feedsViewModel = new ViewModelProvider(PeertubeEditUploadActivity.this).get(TimelineVM.class); feedsViewModel.getMyVideo(null, videoId).observe(PeertubeEditUploadActivity.this, this::manageVIewVideo); + channels = new LinkedHashMap<>(); setTitle(R.string.edit_video); } public void manageUpdate(APIResponse apiResponse) { - set_upload_submit.setEnabled(true); + binding.setUploadSubmit.setEnabled(true); if (apiResponse.getError() != null) { if (apiResponse.getError() != null && apiResponse.getError().getError() != null) Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); @@ -247,7 +222,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { Toasty.error(PeertubeEditUploadActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); else Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - set_upload_submit.setEnabled(true); + binding.setUploadSubmit.setEnabled(true); return; } @@ -263,11 +238,11 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { categoryToSend = video.getCategory(); if (video.getThumbnailPath() != null) { - Helper.loadGiF(PeertubeEditUploadActivity.this, video.getThumbnailPath(), p_video_preview); + Helper.loadGiF(PeertubeEditUploadActivity.this, video.getThumbnailPath(), binding.pVideoPreview); } - set_preview.setOnClickListener(v -> { + binding.setPreview.setOnClickListener(v -> { if (ContextCompat.checkSelfPermission(PeertubeEditUploadActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(PeertubeEditUploadActivity.this, @@ -324,11 +299,28 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { boolean commentEnabled = video.isCommentsEnabled(); boolean isNSFW = video.isNsfw(); - set_upload_enable_comments.setChecked(commentEnabled); - set_upload_nsfw.setChecked(isNSFW); + binding.setUploadEnableComments.setChecked(commentEnabled); + binding.setUploadNsfw.setChecked(isNSFW); - p_video_title.setText(title); - p_video_description.setText(video.getDescription()); + binding.pVideoTitle.setText(title); + binding.pVideoDescription.setText(video.getDescription()); + + new Thread(() -> { + try { + RetrofitPeertubeAPI api; + api = new RetrofitPeertubeAPI(PeertubeEditUploadActivity.this); + VideoData.Description description = api.getVideoDescription(video.getUuid()); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + if (description != null) { + binding.pVideoDescription.setText(description.getDescription()); + } + }; + mainHandler.post(myRunnable); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories()); LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences()); @@ -382,7 +374,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } } //Manage privacies - set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updatePrivacyPosition(position); @@ -393,7 +385,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } }); - set_upload_licenses.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadLicenses.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateLicensePosition(position); @@ -405,7 +397,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } }); //Manage categories - set_upload_categories.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadCategories.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateCategoryPosition(position); @@ -418,7 +410,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { }); //Manage languages - set_upload_languages.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadLanguages.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateLanguagesPosition(position); @@ -430,7 +422,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } }); //Manage languages - set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -447,11 +439,11 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { Item finalLicenseToSend = licenseToSend; ItemStr finalLanguageToSend = languageToSend; Item finalPrivacyToSend = privacyToSend; - set_upload_submit.setOnClickListener(v -> { - String title1 = p_video_title.getText().toString().trim(); - String description = p_video_description.getText().toString().trim(); - boolean isNSFW1 = set_upload_nsfw.isChecked(); - boolean commentEnabled1 = set_upload_enable_comments.isChecked(); + binding.setUploadSubmit.setOnClickListener(v -> { + String title1 = binding.pVideoTitle.getText() != null ? binding.pVideoTitle.getText().toString().trim() : ""; + String description = binding.pVideoDescription.getText() != null ? binding.pVideoDescription.getText().toString().trim() : ""; + boolean isNSFW1 = binding.setUploadNsfw.isChecked(); + boolean commentEnabled1 = binding.setUploadEnableComments.isChecked(); videoParams = new VideoParams(); videoParams.setName(title1); videoParams.setDescription(description); @@ -460,28 +452,27 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { videoParams.setCategory(finalCategoryToSend.getId()); videoParams.setLicence(String.valueOf(finalLicenseToSend.getId())); videoParams.setLanguage(finalLanguageToSend.getId()); - videoParams.setChannelId(channelToSendId); videoParams.setPrivacy(finalPrivacyToSend.getId()); - List tags = p_video_tags.getTags(); + List tags = binding.pVideoTags.getTags(); videoParams.setTags(tags); - set_upload_submit.setEnabled(false); + binding.setUploadSubmit.setEnabled(false); MyVideoVM myVideoVM = new ViewModelProvider(PeertubeEditUploadActivity.this).get(MyVideoVM.class); - myVideoVM.updateVideo(videoId, videoParams, null, null).observe(PeertubeEditUploadActivity.this, this::manageUpdate); + myVideoVM.updateVideo(videoId, videoParams, inputData, inputData).observe(PeertubeEditUploadActivity.this, this::manageUpdate); }); - set_upload_privacy.setSelection(privacyPosition, false); + binding.setUploadPrivacy.setSelection(privacyPosition, false); updatePrivacyPosition(privacyPosition); - set_upload_languages.setSelection(languagePosition, false); + binding.setUploadLanguages.setSelection(languagePosition, false); updateLanguagesPosition(languagePosition); - set_upload_licenses.setSelection(licensePosition, false); + binding.setUploadLicenses.setSelection(licensePosition, false); updateLicensePosition(licensePosition); - set_upload_categories.setSelection(categoryPosition, false); + binding.setUploadCategories.setSelection(categoryPosition, false); updateCategoryPosition(categoryPosition); List tags = video.getTags(); if (tags != null && tags.size() > 0) { - p_video_tags.setTags(tags.toArray(new String[0])); + binding.pVideoTags.setTags(tags.toArray(new String[0])); } } @@ -572,53 +563,12 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show(); return; } - UploadNotificationConfig uploadConfig = new UploadNotificationConfig(); - uploadConfig.getCompleted().autoClear = true; - try { - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - Uri uri = data.getData(); - try { - String uploadId = UUID.randomUUID().toString(); - new MultipartUploadRequest(PeertubeEditUploadActivity.this, uploadId, "https://" + Helper.getLiveInstance(PeertubeEditUploadActivity.this) + "/api/v1/" + String.format("/videos/%s", video.getId())) - .addFileToUpload(uri.toString().replace("file://", ""), "thumbnailfile") - .setMethod("PUT") - .addHeader("Authorization", "Bearer " + token) - .setNotificationConfig(uploadConfig) - .setMaxRetries(2) - .setDelegate(new UploadStatusDelegate() { - @Override - public void onProgress(Context context, UploadInfo uploadInfo) { - // your code here - } - - @Override - public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, - Exception exception) { - Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - } - - @Override - public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { - Glide.with(PeertubeEditUploadActivity.this) - .load(uri) - .into(p_video_preview); - } - - @Override - public void onCancelled(Context context, UploadInfo uploadInfo) { - // your code here - } - }) - .startUpload(); - } catch (FileNotFoundException e) { - Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - e.printStackTrace(); - } - } catch (MalformedURLException e) { - Toasty.error(PeertubeEditUploadActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - e.printStackTrace(); - } + inputData = data.getData(); + Glide.with(PeertubeEditUploadActivity.this) + .load(data.getData()) + .thumbnail(0.1f) + .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) + .into(binding.pVideoPreview); } } @@ -651,7 +601,7 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { } ArrayAdapter adapterChannel = new ArrayAdapter<>(PeertubeEditUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, channelName); - set_upload_channel.setAdapter(adapterChannel); + binding.setUploadChannel.setAdapter(adapterChannel); int channelPosition = 0; if (channels.containsKey(channel.getName())) { LinkedHashMap channelsIterator = new LinkedHashMap<>(channels); @@ -666,9 +616,15 @@ public class PeertubeEditUploadActivity extends AppCompatActivity { channelPosition++; } } - set_upload_channel.setSelection(channelPosition, false); + binding.setUploadChannel.setSelection(channelPosition, false); updateUploadChannel(channelPosition); - set_upload_submit.setEnabled(true); + binding.setUploadSubmit.setEnabled(true); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + finish(); } @SuppressWarnings({"unused", "RedundantSuppression"}) diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java index 9c29462..772397a 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java @@ -38,7 +38,8 @@ import app.fedilab.fedilabtube.client.APIResponse; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.entities.AccountCreation; import app.fedilab.fedilabtube.databinding.ActivityRegisterPeertubeBinding; -import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperAcadInstance; +import app.fedilab.fedilabtube.helper.HelperInstance; import es.dmoral.toasty.Toasty; import static app.fedilab.fedilabtube.MainActivity.PICK_INSTANCE; @@ -49,6 +50,7 @@ public class PeertubeRegisterActivity extends AppCompatActivity { private String instance; private ActivityRegisterPeertubeBinding binding; + @SuppressLint("SetTextI18n") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -61,7 +63,7 @@ public class PeertubeRegisterActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); - if (BuildConfig.full_instances) { + if (BuildConfig.full_instances && BuildConfig.instance_switcher) { binding.loginInstanceContainer.setVisibility(View.VISIBLE); binding.titleLoginInstance.setVisibility(View.VISIBLE); } else { @@ -69,6 +71,10 @@ public class PeertubeRegisterActivity extends AppCompatActivity { binding.titleLoginInstance.setVisibility(View.GONE); } + if (BuildConfig.FLAVOR.compareTo("queermotion") == 0) { + binding.loginInstance.setText("queermotion.org"); + } + binding.username.setOnFocusChangeListener((view, focused) -> { if (!focused && binding.username.getText() != null) { Pattern patternUsername = Pattern.compile("^[a-z0-9._]{1,50}$"); @@ -129,7 +135,7 @@ public class PeertubeRegisterActivity extends AppCompatActivity { } String[] emailArray = binding.email.getText().toString().split("@"); if (!BuildConfig.full_instances) { - if (emailArray.length > 1 && !Arrays.asList(Helper.valideEmails).contains(emailArray[1])) { + if (emailArray.length > 1 && !Arrays.asList(HelperAcadInstance.valideEmails).contains(emailArray[1])) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.email_error_domain, emailArray[1])).show(); return; } @@ -158,7 +164,7 @@ public class PeertubeRegisterActivity extends AppCompatActivity { }); } else { String host = emailArray[1]; - instance = Helper.getPeertubeUrl(host); + instance = HelperInstance.getPeertubeUrl(host); } if (instance != null) { instance = instance.toLowerCase().trim(); diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java index 3bb3f3b..ae2534f 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeUploadActivity.java @@ -21,46 +21,51 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.database.Cursor; +import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Bundle; -import android.provider.OpenableColumns; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.documentfile.provider.DocumentFile; import androidx.lifecycle.ViewModelProvider; -import net.gotev.uploadservice.MultipartUploadRequest; -import net.gotev.uploadservice.UploadNotificationAction; -import net.gotev.uploadservice.UploadNotificationConfig; -import java.io.File; +import net.gotev.uploadservice.data.UploadNotificationAction; +import net.gotev.uploadservice.data.UploadNotificationConfig; +import net.gotev.uploadservice.data.UploadNotificationStatusConfig; +import net.gotev.uploadservice.extensions.ContextExtensionsKt; +import net.gotev.uploadservice.protocols.multipart.MultipartUploadRequest; + +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.UUID; import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.ChannelData; +import app.fedilab.fedilabtube.client.entities.UserMe; +import app.fedilab.fedilabtube.databinding.ActivityPeertubeUploadBinding; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperInstance; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; import es.dmoral.toasty.Toasty; -import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; +import static app.fedilab.fedilabtube.MainActivity.userMe; import static app.fedilab.fedilabtube.client.RetrofitPeertubeAPI.DataType.MY_CHANNELS; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; public class PeertubeUploadActivity extends AppCompatActivity { @@ -68,16 +73,12 @@ public class PeertubeUploadActivity extends AppCompatActivity { public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 724; private final int PICK_IVDEO = 52378; - private Button set_upload_file, set_upload_submit; - private Spinner set_upload_privacy, set_upload_channel; - private TextView set_upload_file_name; - private EditText video_title; private HashMap channels; private Uri uri; private String filename; private HashMap privacyToSend; private HashMap channelToSend; - + private ActivityPeertubeUploadBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { @@ -86,14 +87,60 @@ public class PeertubeUploadActivity extends AppCompatActivity { if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); - setContentView(R.layout.activity_peertube_upload); + binding = ActivityPeertubeUploadBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + + + new Thread(() -> { + UserMe.VideoQuota videoQuotaReply = new RetrofitPeertubeAPI(PeertubeUploadActivity.this).getVideoQuota(); + runOnUiThread(() -> { + if (videoQuotaReply != null) { + long videoQuota = videoQuotaReply.getVideoQuotaUsed(); + long dailyQuota = videoQuotaReply.getVideoQuotaUsedDaily(); + long instanceVideoQuota = userMe.getVideoQuota(); + long instanceDailyQuota = userMe.getVideoQuotaDaily(); + + if (instanceVideoQuota != -1 && instanceVideoQuota != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + binding.totalQuota.setProgress((int) (videoQuota * 100 / instanceVideoQuota), true); + } else { + binding.totalQuota.setProgress((int) (videoQuota * 100 / instanceVideoQuota)); + } + } else { + int progress = videoQuota > 0 ? 30 : 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + binding.totalQuota.setProgress(progress, true); + } else { + binding.totalQuota.setProgress(progress); + } + } + if (instanceDailyQuota != -1 && instanceDailyQuota != 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + binding.dailyQuota.setProgress((int) (dailyQuota * 100 / instanceDailyQuota), true); + } else { + binding.dailyQuota.setProgress((int) (dailyQuota * 100 / instanceDailyQuota)); + } + } else { + int progress = dailyQuota > 0 ? 30 : 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + binding.dailyQuota.setProgress(progress, true); + } else { + binding.dailyQuota.setProgress(progress); + } + } + binding.totalQuotaValue.setText( + String.format(Locale.getDefault(), "%s/%s", + Helper.returnRoundedSize(PeertubeUploadActivity.this, videoQuota), + Helper.returnRoundedSize(PeertubeUploadActivity.this, instanceVideoQuota))); + binding.dailyQuotaValue.setText( + String.format(Locale.getDefault(), "%s/%s", + Helper.returnRoundedSize(PeertubeUploadActivity.this, dailyQuota), + Helper.returnRoundedSize(PeertubeUploadActivity.this, instanceDailyQuota))); + } + }); + }).start(); - set_upload_file = findViewById(R.id.set_upload_file); - set_upload_file_name = findViewById(R.id.set_upload_file_name); - set_upload_channel = findViewById(R.id.set_upload_channel); - set_upload_privacy = findViewById(R.id.set_upload_privacy); - set_upload_submit = findViewById(R.id.set_upload_submit); - video_title = findViewById(R.id.video_title); ChannelsVM viewModelC = new ViewModelProvider(PeertubeUploadActivity.this).get(ChannelsVM.class); viewModelC.get(MY_CHANNELS, null).observe(PeertubeUploadActivity.this, this::manageVIewChannels); @@ -109,33 +156,18 @@ public class PeertubeUploadActivity extends AppCompatActivity { Toasty.error(PeertubeUploadActivity.this, getString(R.string.toot_select_image_error), Toast.LENGTH_LONG).show(); return; } - set_upload_submit.setEnabled(true); - + binding.setUploadSubmit.setEnabled(true); uri = data.getData(); - - String uriString = uri.toString(); - File myFile = new File(uriString); filename = null; - if (uriString.startsWith("content://")) { - Cursor cursor = null; - try { - cursor = getContentResolver().query(uri, null, null, null, null); - if (cursor != null && cursor.moveToFirst()) { - filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } finally { - assert cursor != null; - cursor.close(); - } - } else if (uriString.startsWith("file://")) { - filename = myFile.getName(); + DocumentFile documentFile = DocumentFile.fromSingleUri(this, uri); + if (documentFile != null) { + filename = documentFile.getName(); } if (filename == null) { filename = new Date().toString(); } - set_upload_file_name.setVisibility(View.VISIBLE); - set_upload_file_name.setText(filename); - + binding.setUploadFileName.setVisibility(View.VISIBLE); + binding.setUploadFileName.setText(filename); } } @@ -179,7 +211,7 @@ public class PeertubeUploadActivity extends AppCompatActivity { channelToSend.put(channelName[0], channelId[0]); ArrayAdapter adapterChannel = new ArrayAdapter<>(PeertubeUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, channelName); - set_upload_channel.setAdapter(adapterChannel); + binding.setUploadChannel.setAdapter(adapterChannel); if (peertubeInformation == null) { return; @@ -209,10 +241,10 @@ public class PeertubeUploadActivity extends AppCompatActivity { ArrayAdapter adapterPrivacies = new ArrayAdapter<>(PeertubeUploadActivity.this, android.R.layout.simple_spinner_dropdown_item, privaciesA); - set_upload_privacy.setAdapter(adapterPrivacies); + binding.setUploadPrivacy.setAdapter(adapterPrivacies); //Manage privacies - set_upload_privacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadPrivacy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { LinkedHashMap privaciesCheck = new LinkedHashMap<>(peertubeInformation.getPrivacies()); @@ -235,9 +267,9 @@ public class PeertubeUploadActivity extends AppCompatActivity { } }); - set_upload_file.setEnabled(true); + binding.setUploadFile.setEnabled(true); - set_upload_file.setOnClickListener(v -> { + binding.setUploadFile.setOnClickListener(v -> { if (ContextCompat.checkSelfPermission(PeertubeUploadActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(PeertubeUploadActivity.this, @@ -245,7 +277,7 @@ public class PeertubeUploadActivity extends AppCompatActivity { MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); return; } - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); String[] mimetypes = {"video/*"}; @@ -255,7 +287,7 @@ public class PeertubeUploadActivity extends AppCompatActivity { }); //Manage languages - set_upload_channel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.setUploadChannel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { LinkedHashMap channelsCheck = new LinkedHashMap<>(channels); @@ -279,44 +311,33 @@ public class PeertubeUploadActivity extends AppCompatActivity { } }); - set_upload_submit.setOnClickListener(v -> { + binding.setUploadSubmit.setOnClickListener(v -> { if (uri != null) { Map.Entry channelM = channelToSend.entrySet().iterator().next(); String idChannel = channelM.getValue(); Map.Entry privacyM = privacyToSend.entrySet().iterator().next(); Integer idPrivacy = privacyM.getKey(); - + if (binding.videoTitle.getText() != null && binding.videoTitle.getText().toString().trim().length() > 0) { + filename = binding.videoTitle.getText().toString().trim(); + } try { SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); String token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - UploadNotificationConfig uploadConfig = new UploadNotificationConfig(); - Intent in = new Intent(PeertubeUploadActivity.this, PeertubeEditUploadActivity.class); - PendingIntent clickIntent = PendingIntent.getActivity(PeertubeUploadActivity.this, 1, in, PendingIntent.FLAG_UPDATE_CURRENT); - uploadConfig - .setClearOnActionForAllStatuses(true); - - uploadConfig.getProgress().message = getString(R.string.uploading); - uploadConfig.getCompleted().message = getString(R.string.upload_video_success); - uploadConfig.getError().message = getString(R.string.toast_error); - uploadConfig.getCancelled().message = getString(R.string.toast_cancelled); - uploadConfig.getCompleted().actions.add(new UploadNotificationAction(R.drawable.ic_baseline_check_24, getString(R.string.video_uploaded_action), clickIntent)); - - if (video_title != null && video_title.getText() != null && video_title.getText().toString().trim().length() > 0) { - filename = video_title.getText().toString().trim(); - } - String uploadId = UUID.randomUUID().toString(); - new MultipartUploadRequest(PeertubeUploadActivity.this, uploadId, "https://" + Helper.getLiveInstance(PeertubeUploadActivity.this) + "/api/v1/videos/upload") - .addFileToUpload(uri.toString().replace("file://", ""), "videofile") - .addHeader("Authorization", "Bearer " + token) - .setNotificationConfig(uploadConfig) - .addParameter("name", filename) - .addParameter("channelId", idChannel) + new MultipartUploadRequest(PeertubeUploadActivity.this, "https://" + HelperInstance.getLiveInstance(PeertubeUploadActivity.this) + "/api/v1/videos/upload") + .setMethod("POST") + .setBearerAuth(token) + .addHeader("User-Agent", getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME) .addParameter("privacy", String.valueOf(idPrivacy)) .addParameter("nsfw", "false") + .addParameter("name", filename) .addParameter("commentsEnabled", "true") + .addParameter("downloadEnabled", "true") .addParameter("waitTranscoding", "true") - .setMaxRetries(3) + .addParameter("channelId", idChannel) + .addFileToUpload(uri.toString(), "videofile") + .setNotificationConfig((context, uploadId) -> getNotificationConfig(uploadId)) + .setMaxRetries(2) .startUpload(); finish(); } catch (Exception exc) { @@ -325,4 +346,74 @@ public class PeertubeUploadActivity extends AppCompatActivity { } }); } + + UploadNotificationConfig getNotificationConfig(String uploadId) { + PendingIntent clickIntent = PendingIntent.getActivity( + PeertubeUploadActivity.this, 1, new Intent(this, PeertubeEditUploadActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); + + final boolean autoClear = false; + final boolean clearOnAction = true; + final boolean ringToneEnabled = true; + final ArrayList noActions = new ArrayList<>(1); + + final UploadNotificationAction cancelAction = new UploadNotificationAction( + R.drawable.ic_baseline_cancel_24, + getString(R.string.cancel), + ContextExtensionsKt.getCancelUploadIntent(this, uploadId) + ); + + + final ArrayList progressActions = new ArrayList<>(1); + progressActions.add(cancelAction); + + UploadNotificationStatusConfig progress = new UploadNotificationStatusConfig( + getString(R.string.app_name), + getString(R.string.uploading), + R.drawable.ic_baseline_cloud_upload_24, + Color.BLUE, + null, + clickIntent, + progressActions, + clearOnAction, + autoClear + ); + + UploadNotificationStatusConfig success = new UploadNotificationStatusConfig( + getString(R.string.app_name), + getString(R.string.upload_video_success), + R.drawable.ic_baseline_check_24, + Color.GREEN, + null, + clickIntent, + noActions, + clearOnAction, + autoClear + ); + + + UploadNotificationStatusConfig error = new UploadNotificationStatusConfig( + getString(R.string.app_name), + getString(R.string.toast_error), + R.drawable.ic_baseline_error_24, + Color.RED, + null, + clickIntent, + noActions, + clearOnAction, + autoClear + ); + + UploadNotificationStatusConfig cancelled = new UploadNotificationStatusConfig( + getString(R.string.app_name), + getString(R.string.toast_cancelled), + R.drawable.ic_baseline_cancel_24, + Color.YELLOW, + null, + clickIntent, + noActions, + clearOnAction + ); + + return new UploadNotificationConfig(FedilabTube.UPLOAD_CHANNEL_ID, ringToneEnabled, progress, success, error, cancelled); + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java b/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java index 4df50cf..636ca09 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/SearchActivity.java @@ -16,11 +16,22 @@ package app.fedilab.fedilabtube; import android.os.Bundle; import android.view.MenuItem; +import android.view.View; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentTransaction; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import com.google.android.material.tabs.TabLayout; + +import org.jetbrains.annotations.NotNull; + +import app.fedilab.fedilabtube.databinding.ActivitySearchResultBinding; +import app.fedilab.fedilabtube.fragment.DisplayChannelsFragment; import app.fedilab.fedilabtube.fragment.DisplayVideosFragment; import es.dmoral.toasty.Toasty; @@ -29,12 +40,15 @@ public class SearchActivity extends AppCompatActivity { private String search; + private ActivitySearchResultBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + binding = ActivitySearchResultBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); - setContentView(R.layout.activity_search_result); Bundle b = getIntent().getExtras(); if (b != null) { search = b.getString("search"); @@ -46,15 +60,64 @@ public class SearchActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setTitle(search); + binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.videos))); + binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.channels))); + binding.searchPager.setOffscreenPageLimit(2); + + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + binding.searchPager.setAdapter(mPagerAdapter); + binding.searchPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position); + if (tab != null) + tab.select(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + binding.searchPager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + Fragment fragment = null; + if (binding.searchPager.getAdapter() != null) + fragment = (Fragment) binding.searchPager.getAdapter().instantiateItem(binding.searchPager, tab.getPosition()); + switch (tab.getPosition()) { + case 0: + if (fragment != null) { + DisplayVideosFragment displayVideosFragment = ((DisplayVideosFragment) fragment); + displayVideosFragment.scrollToTop(); + } + break; + case 1: + if (fragment != null) { + DisplayChannelsFragment displayChannelsFragment = ((DisplayChannelsFragment) fragment); + displayChannelsFragment.scrollToTop(); + } + break; + } + } + }); - if (savedInstanceState == null) { - DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); - Bundle bundle = new Bundle(); - bundle.putString("search_peertube", search); - displayVideosFragment.setArguments(bundle); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(R.id.container, displayVideosFragment).commit(); - } } @@ -67,4 +130,37 @@ public class SearchActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } + + /** + * Pager adapter for the 2 fragments + */ + private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NotNull + @Override + public Fragment getItem(int position) { + Bundle bundle = new Bundle(); + if (position == 0) { + DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); + bundle.putString("search_peertube", search); + displayVideosFragment.setArguments(bundle); + return displayVideosFragment; + } + DisplayChannelsFragment displayChannelsFragment = new DisplayChannelsFragment(); + bundle.putString("search_peertube", search); + displayChannelsFragment.setArguments(bundle); + return displayChannelsFragment; + } + + + @Override + public int getCount() { + return 2; + } + } + } diff --git a/app/src/main/java/app/fedilab/fedilabtube/SepiaSearchActivity.java b/app/src/main/java/app/fedilab/fedilabtube/SepiaSearchActivity.java index 618fd9b..b69e922 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/SepiaSearchActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/SepiaSearchActivity.java @@ -21,12 +21,8 @@ import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.RadioGroup; -import android.widget.Spinner; import androidx.appcompat.app.AppCompatActivity; -import androidx.constraintlayout.widget.ConstraintLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; @@ -42,27 +38,29 @@ import java.util.List; import java.util.Map; import app.fedilab.fedilabtube.client.entities.SepiaSearch; +import app.fedilab.fedilabtube.databinding.ActivitySepiaSearchBinding; import app.fedilab.fedilabtube.fragment.DisplaySepiaSearchFragment; import app.fedilab.fedilabtube.helper.Helper; -import mabbas007.tagsedittext.TagsEditText; -import static app.fedilab.fedilabtube.MainActivity.peertubeInformation; import static app.fedilab.fedilabtube.PeertubeActivity.hideKeyboard; +import static app.fedilab.fedilabtube.helper.Helper.peertubeInformation; public class SepiaSearchActivity extends AppCompatActivity { private SepiaSearch sepiaSearchVideo, sepiaSearchChannel; - private TagsEditText sepia_element_all_of_tags, sepia_element_one_of_tags; - private MaterialSearchBar searchBar; - private ConstraintLayout filter_elements; + + private ActivitySepiaSearchBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sepia_search); + binding = ActivitySepiaSearchBinding.inflate(getLayoutInflater()); + View rootView = binding.getRoot(); + setContentView(rootView); + sepiaSearchVideo = new SepiaSearch(); sepiaSearchChannel = new SepiaSearch(); @@ -76,21 +74,18 @@ public class SepiaSearchActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); - Button filter = findViewById(R.id.filter); - filter_elements = findViewById(R.id.filter_elements); - filter.setOnClickListener(view -> { - if (filter_elements.getVisibility() == View.VISIBLE) { - filter_elements.setVisibility(View.GONE); + binding.filter.setOnClickListener(view -> { + if (binding.filterElements.getVisibility() == View.VISIBLE) { + binding.filterElements.setVisibility(View.GONE); } else { - filter_elements.setVisibility(View.VISIBLE); + binding.filterElements.setVisibility(View.VISIBLE); } }); - RadioGroup sepia_element_nsfw = findViewById(R.id.sepia_element_nsfw); - sepia_element_nsfw.setOnCheckedChangeListener((group, checkedId) -> sepiaSearchVideo.setNsfw(checkedId != R.id.sepia_element_nsfw_no)); - RadioGroup radio_date = findViewById(R.id.radio_date); - radio_date.setOnCheckedChangeListener((group, checkedId) -> { + binding.sepiaElementNsfw.setOnCheckedChangeListener((group, checkedId) -> sepiaSearchVideo.setNsfw(checkedId != R.id.sepia_element_nsfw_no)); + + binding.radioDate.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.sepia_element_published_date_today) { Calendar cal = GregorianCalendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, 0); @@ -122,8 +117,7 @@ public class SepiaSearchActivity extends AppCompatActivity { }); - RadioGroup duration = findViewById(R.id.duration); - duration.setOnCheckedChangeListener((group, checkedId) -> { + binding.duration.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.sepia_element_duration_short) { sepiaSearchVideo.setDurationMin(0); sepiaSearchVideo.setDurationMax(240); @@ -140,11 +134,10 @@ public class SepiaSearchActivity extends AppCompatActivity { }); - Spinner sort_by = findViewById(R.id.sort_by); ArrayAdapter adapterSortBy = new ArrayAdapter<>(SepiaSearchActivity.this, android.R.layout.simple_spinner_dropdown_item, getResources().getStringArray(R.array.sort_by_array)); - sort_by.setAdapter(adapterSortBy); - sort_by.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.sortBy.setAdapter(adapterSortBy); + binding.sortBy.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { String orderby, channelOrderBy; @@ -171,12 +164,6 @@ public class SepiaSearchActivity extends AppCompatActivity { } }); - Spinner sepia_element_category = findViewById(R.id.sepia_element_category); - Spinner sepia_element_license = findViewById(R.id.sepia_element_license); - Spinner sepia_element_language = findViewById(R.id.sepia_element_language); - - sepia_element_all_of_tags = findViewById(R.id.sepia_element_all_of_tags); - sepia_element_one_of_tags = findViewById(R.id.sepia_element_one_of_tags); LinkedHashMap categories = new LinkedHashMap<>(peertubeInformation.getCategories()); LinkedHashMap licences = new LinkedHashMap<>(peertubeInformation.getLicences()); @@ -203,7 +190,7 @@ public class SepiaSearchActivity extends AppCompatActivity { } ArrayAdapter adapterCatgories = new ArrayAdapter<>(SepiaSearchActivity.this, android.R.layout.simple_spinner_dropdown_item, categoriesA); - sepia_element_category.setAdapter(adapterCatgories); + binding.sepiaElementCategory.setAdapter(adapterCatgories); //Populate licenses @@ -222,7 +209,7 @@ public class SepiaSearchActivity extends AppCompatActivity { } ArrayAdapter adapterLicenses = new ArrayAdapter<>(SepiaSearchActivity.this, android.R.layout.simple_spinner_dropdown_item, licensesA); - sepia_element_license.setAdapter(adapterLicenses); + binding.sepiaElementLicense.setAdapter(adapterLicenses); //Populate languages String[] languagesA = new String[languages.size() + 1]; @@ -240,10 +227,10 @@ public class SepiaSearchActivity extends AppCompatActivity { } ArrayAdapter adapterLanguages = new ArrayAdapter<>(SepiaSearchActivity.this, android.R.layout.simple_spinner_dropdown_item, languagesA); - sepia_element_language.setAdapter(adapterLanguages); + binding.sepiaElementLanguage.setAdapter(adapterLanguages); - sepia_element_license.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.sepiaElementLicense.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateLicensePosition(position); @@ -255,7 +242,7 @@ public class SepiaSearchActivity extends AppCompatActivity { } }); //Manage categories - sepia_element_category.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.sepiaElementCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateCategoryPosition(position); @@ -268,7 +255,7 @@ public class SepiaSearchActivity extends AppCompatActivity { }); //Manage languages - sepia_element_language.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.sepiaElementLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateLanguagesPosition(position); @@ -281,9 +268,7 @@ public class SepiaSearchActivity extends AppCompatActivity { }); - searchBar = findViewById(R.id.searchBar); - - searchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() { + binding.searchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() { @Override public void onSearchStateChanged(boolean enabled) { @@ -299,22 +284,21 @@ public class SepiaSearchActivity extends AppCompatActivity { makeSearch(); } }); - Button apply_filter = findViewById(R.id.apply_filter); - apply_filter.setOnClickListener(v -> makeSearch()); + binding.applyFilter.setOnClickListener(v -> makeSearch()); - searchBar.openSearch(); + binding.searchBar.openSearch(); } private void makeSearch() { hideKeyboard(SepiaSearchActivity.this); sepiaSearchVideo.setStart("0"); - if (sepia_element_one_of_tags.getTags().size() > 0) { - sepiaSearchVideo.setTagsOneOf(sepia_element_one_of_tags.getTags()); + if (binding.sepiaElementOneOfTags.getTags().size() > 0) { + sepiaSearchVideo.setTagsOneOf(binding.sepiaElementOneOfTags.getTags()); } else { sepiaSearchVideo.setTagsOneOf(null); } - if (sepia_element_all_of_tags.getTags().size() > 0) { - sepiaSearchVideo.setTagsAllOf(sepia_element_all_of_tags.getTags()); + if (binding.sepiaElementAllOfTags.getTags().size() > 0) { + sepiaSearchVideo.setTagsAllOf(binding.sepiaElementAllOfTags.getTags()); } else { sepiaSearchVideo.setTagsAllOf(null); } @@ -322,8 +306,8 @@ public class SepiaSearchActivity extends AppCompatActivity { Fragment fragment = getSupportFragmentManager().findFragmentByTag("SEPIA_SEARCH"); if (fragment != null) getSupportFragmentManager().beginTransaction().remove(fragment).commit(); - filter_elements.setVisibility(View.GONE); - sepiaSearchVideo.setSearch(searchBar.getText()); + binding.filterElements.setVisibility(View.GONE); + sepiaSearchVideo.setSearch(binding.searchBar.getText()); DisplaySepiaSearchFragment displaySepiaSearchFragment = new DisplaySepiaSearchFragment(); Bundle bundle = new Bundle(); bundle.putParcelable("sepiaSearchVideo", sepiaSearchVideo); diff --git a/app/src/main/java/app/fedilab/fedilabtube/ShowAccountActivity.java b/app/src/main/java/app/fedilab/fedilabtube/ShowAccountActivity.java index e9f41ca..259d2d2 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/ShowAccountActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/ShowAccountActivity.java @@ -288,13 +288,12 @@ public class ShowAccountActivity extends AppCompatActivity { if (position == 0) { DisplayChannelsFragment displayChannelsFragment = new DisplayChannelsFragment(); bundle.putString("name", account.getAcct()); - bundle.putBoolean("myChannels", false); displayChannelsFragment.setArguments(bundle); return displayChannelsFragment; } DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.ACCOUNT_VIDEOS); - bundle.putString("channelId", account.getAcct()); + bundle.putParcelable("account", account); bundle.putString("peertube_instance", account.getHost()); displayVideosFragment.setArguments(bundle); return displayVideosFragment; diff --git a/app/src/main/java/app/fedilab/fedilabtube/ShowChannelActivity.java b/app/src/main/java/app/fedilab/fedilabtube/ShowChannelActivity.java index faae6f7..a47deb1 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/ShowChannelActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/ShowChannelActivity.java @@ -157,18 +157,23 @@ public class ShowChannelActivity extends AppCompatActivity { account_follow.setOnClickListener(v -> { AlertDialog.Builder builderSingle = new AlertDialog.Builder(ShowChannelActivity.this); builderSingle.setTitle(getString(R.string.list_of_accounts)); - if (accounts != null) { - final OwnAccountsAdapter accountsListAdapter = new OwnAccountsAdapter(ShowChannelActivity.this, accounts); - builderSingle.setAdapter(accountsListAdapter, (dialog, which) -> { - new Thread(() -> { - try { - RetrofitPeertubeAPI peertubeAPI = new RetrofitPeertubeAPI(ShowChannelActivity.this, accounts.get(which).getHost(), accounts.get(which).getToken()); - peertubeAPI.post(FOLLOW, channel.getAcct(), null); - } catch (Exception e) { - e.printStackTrace(); - } - }).start(); - }); + if (accounts != null && accounts.size() > 0) { + if (accounts.size() > 1) { + final OwnAccountsAdapter accountsListAdapter = new OwnAccountsAdapter(ShowChannelActivity.this, accounts); + builderSingle.setAdapter(accountsListAdapter, (dialog, which) -> { + new Thread(() -> { + try { + RetrofitPeertubeAPI peertubeAPI = new RetrofitPeertubeAPI(ShowChannelActivity.this, accounts.get(which).getHost(), accounts.get(which).getToken()); + peertubeAPI.post(FOLLOW, channel.getAcct(), null); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + }); + } else { + RetrofitPeertubeAPI peertubeAPI = new RetrofitPeertubeAPI(ShowChannelActivity.this, accounts.get(0).getHost(), accounts.get(0).getToken()); + peertubeAPI.post(FOLLOW, channel.getAcct(), null); + } } builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderSingle.show(); @@ -493,7 +498,7 @@ public class ShowChannelActivity extends AppCompatActivity { DisplayVideosFragment displayVideosFragment = new DisplayVideosFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(Helper.TIMELINE_TYPE, TimelineVM.TimelineType.CHANNEL_VIDEOS); - bundle.putString("channelId", channel.getAcct()); + bundle.putParcelable("channel", channel); bundle.putString("peertube_instance", channel.getHost()); bundle.putBoolean("sepia_search", sepiaSearch); displayVideosFragment.setArguments(bundle); diff --git a/app/src/main/java/app/fedilab/fedilabtube/VideosTimelineActivity.java b/app/src/main/java/app/fedilab/fedilabtube/VideosTimelineActivity.java index 211b336..3aff82b 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/VideosTimelineActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/VideosTimelineActivity.java @@ -33,7 +33,7 @@ import java.util.GregorianCalendar; import java.util.Locale; import app.fedilab.fedilabtube.client.APIResponse; -import app.fedilab.fedilabtube.databinding.ActivitySearchResultBinding; +import app.fedilab.fedilabtube.databinding.ActivityVideosTimelineBinding; import app.fedilab.fedilabtube.fragment.DisplayVideosFragment; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.viewmodel.TimelineVM; @@ -50,7 +50,7 @@ public class VideosTimelineActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivitySearchResultBinding binding = ActivitySearchResultBinding.inflate(getLayoutInflater()); + ActivityVideosTimelineBinding binding = ActivityVideosTimelineBinding.inflate(getLayoutInflater()); View mainView = binding.getRoot(); setContentView(mainView); @@ -131,6 +131,11 @@ public class VideosTimelineActivity extends AppCompatActivity { } + @Override + public void onBackPressed() { + super.onBackPressed(); + finish(); + } @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/MenuItemVideo.java b/app/src/main/java/app/fedilab/fedilabtube/client/MenuItemVideo.java new file mode 100644 index 0000000..1c5465b --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/client/MenuItemVideo.java @@ -0,0 +1,53 @@ +package app.fedilab.fedilabtube.client; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +public class MenuItemVideo { + + int icon; + String title; + actionType action; + + public int getIcon() { + return icon; + } + + public void setIcon(int icon) { + this.icon = icon; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public actionType getAction() { + return action; + } + + public void setAction(actionType action) { + this.action = action; + } + + public enum actionType { + RESOLUTION, + SPEED, + CAPTION, + AUTONEXT + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java index 7592ad8..6286f7b 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java @@ -60,7 +60,11 @@ import retrofit2.http.QueryMap; public interface PeertubeService { @GET("instances") - Call getInstances(@QueryMap Map params, @Query("nsfwPolicy[]") String nsfwPolicy, @Query("categoriesOr[]") List categories, @Query("languagesOr[]") List languages); + Call getInstances( + @QueryMap Map params, + @Query("nsfwPolicy[]") String nsfwPolicy, + @Query("categoriesOr[]") List categories, + @Query("languagesOr[]") List languages); //Server settings @GET(".well-known/nodeinfo") @@ -70,6 +74,10 @@ public interface PeertubeService { @GET("config/about") Call configAbout(); + //Instance config + @GET("config") + Call config(); + @GET("{nodeInfoPath}") Call getNodeinfo(@Path(value = "nodeInfoPath", encoded = true) String nodeInfoPath); @@ -120,6 +128,9 @@ public interface PeertubeService { Call verifyCredentials(@Header("Authorization") String credentials); + @GET("users/me/video-quota-used") + Call getVideoQuota(@Header("Authorization") String credentials); + @FormUrlEncoded @PUT("videos/{id}/watching") Call addToHistory( @@ -142,6 +153,13 @@ public interface PeertubeService { @Field("nsfwPolicy") String nsfwPolicy ); + @Multipart + @POST("video-channels/{channelHandle}/avatar/pick") + Call updateChannelProfilePicture( + @Header("Authorization") String credentials, + @Path("channelHandle") String channelHandle, + @Part MultipartBody.Part avatarfile); + @Multipart @POST("users/me/avatar/pick") Call updateProfilePicture( @@ -187,7 +205,7 @@ public interface PeertubeService { Call deleteHistory( @Header("Authorization") String credentials); - //Search + //Search videos @GET("search/videos") Call searchVideos( @Header("Authorization") String credentials, @@ -195,6 +213,14 @@ public interface PeertubeService { @Query("start") String maxId, @Query("count") String count); + //Search channels + @GET("search/video-channels") + Call searchChannels( + @Header("Authorization") String credentials, + @Query("search") String search, + @Query("searcharget") String searchTarget, + @Query("start") String maxId, + @Query("count") String count); //Search @GET("search/videos") diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java index 1e4f41b..846e8d4 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java @@ -19,12 +19,18 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.webkit.MimeTypeMap; +import androidx.documentfile.provider.DocumentFile; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -68,6 +74,7 @@ import app.fedilab.fedilabtube.client.entities.UserSettings; import app.fedilab.fedilabtube.client.entities.VideoParams; import app.fedilab.fedilabtube.client.entities.WellKnownNodeinfo; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.HelperInstance; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; import app.fedilab.fedilabtube.viewmodel.ChannelsVM; @@ -95,8 +102,8 @@ public class RetrofitPeertubeAPI { public RetrofitPeertubeAPI(Context context) { _context = context; - instance = Helper.getLiveInstance(context); - finalUrl = "https://" + Helper.getLiveInstance(context) + "/api/v1/"; + instance = HelperInstance.getLiveInstance(context); + finalUrl = "https://" + HelperInstance.getLiveInstance(context) + "/api/v1/"; SharedPreferences sharedpreferences = _context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); count = String.valueOf(sharedpreferences.getInt(Helper.SET_VIDEOS_PER_PAGE, Helper.VIDEOS_PER_PAGE)); } @@ -118,7 +125,7 @@ public class RetrofitPeertubeAPI { if (host.startsWith("tube") || BuildConfig.full_instances) { instance = host; } else { - instance = Helper.getPeertubeUrl(host); + instance = HelperInstance.getPeertubeUrl(host); } try { UserMe userMe = new RetrofitPeertubeAPI(activity, instance, token).verifyCredentials(); @@ -559,22 +566,7 @@ public class RetrofitPeertubeAPI { throw error; } if (userSettings.getAvatarfile() != null) { - InputStream inputStream = _context.getContentResolver().openInputStream(userSettings.getAvatarfile()); - ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); - int bufferSize = 1024; - byte[] buffer = new byte[bufferSize]; - - int len; - while ((len = inputStream.read(buffer)) != -1) { - byteBuffer.write(buffer, 0, len); - } - byte[] imageBytes = byteBuffer.toByteArray(); - String mime = MimeTypeMap.getFileExtensionFromUrl(userSettings.getAvatarfile().toString()); - if (mime == null || mime.trim().length() == 0) { - mime = "png"; - } - RequestBody requestFile = RequestBody.create(MediaType.parse("image/" + mime), imageBytes); - MultipartBody.Part bodyThumbnail = MultipartBody.Part.createFormData("avatarfile", userSettings.getFileName(), requestFile); + MultipartBody.Part bodyThumbnail = createFile("avatarfile", userSettings.getAvatarfile(), userSettings.getFileName()); Call updateProfilePicture = peertubeService.updateProfilePicture(getToken(), bodyThumbnail); Response responseAvatar = updateProfilePicture.execute(); if (response.isSuccessful()) { @@ -594,6 +586,29 @@ public class RetrofitPeertubeAPI { return avatarResponse; } + private MultipartBody.Part createFile(@NotNull String paramName, @NotNull Uri uri, String filename) throws IOException { + + InputStream inputStream = _context.getContentResolver().openInputStream(uri); + ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); + int bufferSize = 1024; + byte[] buffer = new byte[bufferSize]; + + int len; + while ((len = inputStream.read(buffer)) != -1) { + byteBuffer.write(buffer, 0, len); + } + byte[] imageBytes = byteBuffer.toByteArray(); + String mime = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); + if (mime == null || mime.trim().length() == 0) { + mime = "png"; + } + if (filename == null) { + filename = "my_image." + mime; + } + RequestBody requestFile = RequestBody.create(imageBytes, MediaType.parse("image/" + mime)); + return MultipartBody.Part.createFormData(paramName, filename, requestFile); + } + /** * Check if users via their uris are following the authenticated user * @@ -662,6 +677,47 @@ public class RetrofitPeertubeAPI { return null; } + /** + * Config of the instance + * + * @return InstanceConfig + */ + public InstanceData.InstanceConfig getConfigInstance() { + + PeertubeService peertubeService = init(); + Call config = peertubeService.config(); + try { + Response response = config.execute(); + if (response.isSuccessful() && response.body() != null) { + return response.body(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + + /** + * Get video quota + * + * @return UserMe.VideoQuota + */ + public UserMe.VideoQuota getVideoQuota() { + + PeertubeService peertubeService = init(); + Call videoQuotaCall = peertubeService.getVideoQuota(getToken()); + try { + Response response = videoQuotaCall.execute(); + if (response.isSuccessful() && response.body() != null) { + return response.body(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + /** * Returns informations about Peertube such privacies, licenses, etc. * @@ -815,7 +871,7 @@ public class RetrofitPeertubeAPI { params.put("count", "250"); params.put("healthy", "true"); params.put("signup", "true"); - params.put("minUserQuota", instanceParams.getMinUserQuota()); + params.put("sort", "-totalUsers"); Call instancesCall = peertubeService.getInstances(params, instanceParams.getNsfwPolicy(), instanceParams.getCategoriesOr(), instanceParams.getLanguagesOr()); APIResponse apiResponse = new APIResponse(); try { @@ -876,6 +932,7 @@ public class RetrofitPeertubeAPI { APIResponse apiResponse = new APIResponse(); try { Response response = searchVideosCall.execute(); + if (response.isSuccessful() && response.body() != null) { apiResponse.setPeertubes(response.body().data); } else { @@ -890,6 +947,34 @@ public class RetrofitPeertubeAPI { return apiResponse; } + + /** + * Retrieves channels search *synchronously* + * + * @param query String search + * @return APIResponse + */ + public APIResponse searchChannels(String query, String max_id) { + PeertubeService peertubeService = init(); + Call searchChannelsCall = peertubeService.searchChannels(getToken(), query, "local", max_id, count); + APIResponse apiResponse = new APIResponse(); + try { + Response response = searchChannelsCall.execute(); + if (response.isSuccessful() && response.body() != null) { + apiResponse.setChannels(response.body().data); + } else { + setError(apiResponse, response.code(), response.errorBody()); + } + } catch (IOException e) { + Error error = new Error(); + error.setError(_context.getString(R.string.toast_error)); + apiResponse.setError(error); + e.printStackTrace(); + } + return apiResponse; + } + + /*** * Verifiy credential of the authenticated user *synchronously* * @return Account @@ -949,34 +1034,47 @@ public class RetrofitPeertubeAPI { * @param previewfile File preview * @return APIResponse */ - public APIResponse updateVideo(String videoId, VideoParams videoParams, File thumbnail, File previewfile) { + public APIResponse updateVideo(String videoId, VideoParams videoParams, Uri thumbnail, Uri previewfile) { PeertubeService peertubeService = init(); + MultipartBody.Part bodyThumbnail = null; MultipartBody.Part bodyPreviewfile = null; - if (thumbnail != null) { - RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), thumbnail); - bodyThumbnail = MultipartBody.Part.createFormData("image", thumbnail.getName(), requestFile); + try { + if (thumbnail != null) { + DocumentFile documentFile = DocumentFile.fromSingleUri(_context, thumbnail); + String thumbnailName = null; + if (documentFile != null) { + thumbnailName = documentFile.getName(); + } + bodyThumbnail = createFile("avatarfile", thumbnail, thumbnailName); + } + if (previewfile != null && thumbnail != null) { + DocumentFile documentFile = DocumentFile.fromSingleUri(_context, thumbnail); + String previewfileName = null; + if (documentFile != null) { + previewfileName = documentFile.getName(); + } + bodyPreviewfile = createFile("image", previewfile, previewfileName); + } + } catch (IOException e) { + e.printStackTrace(); } - if (previewfile != null) { - RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), previewfile); - bodyPreviewfile = MultipartBody.Part.createFormData("image", previewfile.getName(), requestFile); - } - RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), videoParams.getChannelId()); - RequestBody description = RequestBody.create(MediaType.parse("text/plain"), videoParams.getDescription()); - RequestBody language = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLanguage()); - RequestBody license = RequestBody.create(MediaType.parse("text/plain"), videoParams.getLicence()); - RequestBody name = RequestBody.create(MediaType.parse("text/plain"), videoParams.getName()); + RequestBody channelId = RequestBody.create(videoParams.getChannelId(), MediaType.parse("text/plain")); + RequestBody description = RequestBody.create(videoParams.getDescription(), MediaType.parse("text/plain")); + RequestBody language = RequestBody.create(videoParams.getLanguage(), MediaType.parse("text/plain")); + RequestBody license = RequestBody.create(videoParams.getLicence(), MediaType.parse("text/plain")); + RequestBody name = RequestBody.create(videoParams.getName(), MediaType.parse("text/plain")); List tags = null; if (videoParams.getTags() != null && videoParams.getTags().size() > 0) { tags = new ArrayList<>(); for (String tag : videoParams.getTags()) { - tags.add(RequestBody.create(MediaType.parse("text/plain"), tag)); + tags.add(RequestBody.create(tag, MediaType.parse("text/plain"))); } } RequestBody support = null; if (videoParams.getSupport() != null) { - support = RequestBody.create(MediaType.parse("text/plain"), videoParams.getSupport()); + support = RequestBody.create(videoParams.getSupport(), MediaType.parse("text/plain")); } @@ -1187,7 +1285,7 @@ public class RetrofitPeertubeAPI { * @param channelParams PlaylistParams * @return APIResponse */ - public APIResponse createOrUpdateChannel(ChannelsVM.action apiAction, String channelId, ChannelParams channelParams) { + public APIResponse createOrUpdateChannel(ChannelsVM.action apiAction, String channelId, ChannelParams channelParams, Uri avatar) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); @@ -1210,6 +1308,21 @@ public class RetrofitPeertubeAPI { setError(apiResponse, response.code(), response.errorBody()); } } + if (avatar != null) { + DocumentFile documentFile = DocumentFile.fromSingleUri(_context, avatar); + String avatarfileName = null; + if (documentFile != null) { + avatarfileName = documentFile.getName(); + } + MultipartBody.Part bodyThumbnail = createFile("avatarfile", avatar, avatarfileName); + Call updateProfilePicture = peertubeService.updateChannelProfilePicture(getToken(), channelId, bodyThumbnail); + Response responseAvatar = updateProfilePicture.execute(); + if (responseAvatar.isSuccessful()) { + UserMe.AvatarResponse avatarResponse = responseAvatar.body(); + } else { + setError(apiResponse, responseAvatar.code(), responseAvatar.errorBody()); + } + } } catch (IOException e) { Error error = new Error(); error.setError(_context.getString(R.string.toast_error)); @@ -1333,21 +1446,31 @@ public class RetrofitPeertubeAPI { * @param playlistParams PlaylistParams * @return APIResponse */ - public APIResponse createOrUpdatePlaylist(PlaylistsVM.action apiAction, String playlistId, PlaylistParams playlistParams, File thumbnail) { + public APIResponse createOrUpdatePlaylist(PlaylistsVM.action apiAction, String playlistId, PlaylistParams playlistParams, Uri thumbnail) { PeertubeService peertubeService = init(); APIResponse apiResponse = new APIResponse(); MultipartBody.Part body = null; + + MultipartBody.Part bodyThumbnail = null; if (thumbnail != null) { - RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), thumbnail); - body = MultipartBody.Part.createFormData("image", thumbnail.getName(), requestFile); + DocumentFile documentFile = DocumentFile.fromSingleUri(_context, thumbnail); + String avatarfileName = null; + if (documentFile != null) { + avatarfileName = documentFile.getName(); + } + try { + bodyThumbnail = createFile("thumbnailfile", thumbnail, avatarfileName); + } catch (IOException e) { + e.printStackTrace(); + } } try { - RequestBody displayName = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDisplayName()); - RequestBody description = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getDescription()); - RequestBody channelId = RequestBody.create(MediaType.parse("text/plain"), playlistParams.getVideoChannelId()); + RequestBody displayName = RequestBody.create(playlistParams.getDisplayName(), MediaType.parse("text/plain")); + RequestBody description = RequestBody.create(playlistParams.getDescription(), MediaType.parse("text/plain")); + RequestBody channelId = RequestBody.create(playlistParams.getVideoChannelId(), MediaType.parse("text/plain")); if (apiAction == PlaylistsVM.action.CREATE_PLAYLIST) { - Call stringCall = peertubeService.addPlaylist(getToken(), displayName, description, playlistParams.getPrivacy(), channelId, body); + Call stringCall = peertubeService.addPlaylist(getToken(), displayName, description, playlistParams.getPrivacy(), channelId, bodyThumbnail); Response response = stringCall.execute(); if (response.isSuccessful() && response.body() != null) { apiResponse.setActionReturn(response.body().getVideoPlaylist().getId()); @@ -1356,7 +1479,7 @@ public class RetrofitPeertubeAPI { } } else if (apiAction == PlaylistsVM.action.UPDATE_PLAYLIST) { - Call stringCall = peertubeService.updatePlaylist(getToken(), playlistId, displayName, description, playlistParams.getPrivacy(), channelId, body); + Call stringCall = peertubeService.updatePlaylist(getToken(), playlistId, displayName, description, playlistParams.getPrivacy(), channelId, bodyThumbnail); Response response = stringCall.execute(); if (response.isSuccessful()) { apiResponse.setActionReturn(response.body()); @@ -1621,10 +1744,10 @@ public class RetrofitPeertubeAPI { * @param id String id * @return APIResponse */ - public APIResponse getVideos(String id, boolean myVideo) { + public APIResponse getVideos(String id, boolean myVideo, boolean canUseToken) { PeertubeService peertubeService = init(); Call video; - if (myVideo) { + if (myVideo || canUseToken) { video = peertubeService.getMyVideo(getToken(), id); } else { video = peertubeService.getVideo(id); @@ -1632,19 +1755,49 @@ public class RetrofitPeertubeAPI { APIResponse apiResponse = new APIResponse(); try { Response response = video.execute(); - if (response.isSuccessful()) { List videos = new ArrayList<>(); videos.add(response.body()); apiResponse.setPeertubes(videos); } else { - Error error = new Error(); - error.setStatusCode(response.code()); + if (response.errorBody() != null) { - error.setError(response.errorBody().string()); + + String error = response.errorBody().string(); + if (error.contains("originUrl")) { + try { + JSONObject jsonObject = new JSONObject(error); + List videos = new ArrayList<>(); + VideoData.Video videoRedirect = new VideoData.Video(); + videoRedirect.setErrorCode(jsonObject.getInt("errorCode")); + videoRedirect.setOriginUrl(jsonObject.getString("originUrl")); + videos.add(videoRedirect); + apiResponse.setPeertubes(videos); + } catch (JSONException e) { + e.printStackTrace(); + } + } else if (error.contains("error")) { + try { + JSONObject jsonObject = new JSONObject(error); + List videos = new ArrayList<>(); + VideoData.Video videoErrorMessage = new VideoData.Video(); + videoErrorMessage.setErrorMessage(jsonObject.getString("error")); + videos.add(videoErrorMessage); + apiResponse.setPeertubes(videos); + } catch (JSONException e) { + e.printStackTrace(); + } + } } else { - error.setError(_context.getString(R.string.toast_error)); + Error error = new Error(); + error.setStatusCode(response.code()); + if (response.errorBody() != null) { + error.setError(response.errorBody().string()); + } else { + error.setError(_context.getString(R.string.toast_error)); + } } + } } catch (IOException e) { Error error = new Error(); diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java b/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java index 59eff4c..d802489 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java @@ -64,6 +64,7 @@ public class InstanceData { @SerializedName("isNSFW") private boolean isNSFW; private SpannableStringBuilder spannableStringBuilder; + private boolean truncatedDescription = true; public boolean isAutoBlacklistUserVideosEnabled() { return autoBlacklistUserVideosEnabled; @@ -240,6 +241,14 @@ public class InstanceData { public void setCategories(List categories) { this.categories = categories; } + + public boolean isTruncatedDescription() { + return truncatedDescription; + } + + public void setTruncatedDescription(boolean truncatedDescription) { + this.truncatedDescription = truncatedDescription; + } } public static class InstanceInfo { @@ -277,6 +286,7 @@ public class InstanceData { @SerializedName("terms") private String terms; private String host; + private boolean truncatedDescription = true; public AboutInstance() { } @@ -329,6 +339,14 @@ public class InstanceData { this.host = host; } + public boolean isTruncatedDescription() { + return truncatedDescription; + } + + public void setTruncatedDescription(boolean truncatedDescription) { + this.truncatedDescription = truncatedDescription; + } + @Override public int describeContents() { return 0; @@ -343,5 +361,42 @@ public class InstanceData { parcel.writeString(host); } } + + + public static class InstanceConfig { + @SerializedName("user") + private User user; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + } + + public static class User { + @SerializedName("videoQuota") + private long videoQuota; + @SerializedName("videoQuotaDaily") + private long videoQuotaDaily; + + public long getVideoQuota() { + return videoQuota; + } + + public void setVideoQuota(long videoQuota) { + this.videoQuota = videoQuota; + } + + public long getVideoQuotaDaily() { + return videoQuotaDaily; + } + + public void setVideoQuotaDaily(long videoQuotaDaily) { + this.videoQuotaDaily = videoQuotaDaily; + } + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/data/VideoData.java b/app/src/main/java/app/fedilab/fedilabtube/client/data/VideoData.java index 2a8676e..7bf47d4 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/data/VideoData.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/data/VideoData.java @@ -43,6 +43,7 @@ public class VideoData { @SerializedName("data") public List