Compare commits

..

No commits in common. "main" and "transcoding-upstream" have entirely different histories.

350 changed files with 2997 additions and 6693 deletions

View file

@ -5,27 +5,9 @@ on:
pull_request: pull_request:
jobs: jobs:
# lint: lint:
# name: Lint name: Lint
# runs-on: ubuntu-22.04
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Validate Gradle Wrapper
# uses: gradle/actions/wrapper-validation@v3
# - name: Set up JDK 17
# uses: actions/setup-java@v4
# with:
# java-version: 17
# distribution: temurin
# - name: Setup Gradle
# uses: gradle/actions/setup-gradle@v3
# - name: Build with Gradle
# run: ./gradlew lintDebug ktlintCheck
assemble:
name: Assemble
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
if: startsWith(github.event.head_commit.message, 'build:')
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -39,45 +21,62 @@ jobs:
- name: Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v3 uses: gradle/actions/setup-gradle@v3
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew assemble run: ./gradlew lintDebug ktlintCheck
assemble:
name: Assemble
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- name: Build with Gradle
run: ./gradlew assembleDebug
# Upload all build artifacts in separate steps. This can be shortened once https://github.com/actions/upload-artifact/pull/354 is merged. # Upload all build artifacts in separate steps. This can be shortened once https://github.com/actions/upload-artifact/pull/354 is merged.
- name: Upload artifact ananas-v0.10.3-0.14.2-libre-arm64-v8a.apk - name: Upload artifact phone-libre-arm64-v8a-debug.apk
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: phone-libre-arm64-v8a.apk name: phone-libre-arm64-v8a-debug.apk
path: ./app/phone/build/outputs/apk/libre/release/ananas-v0.10.3-0.14.2-libre-arm64-v8a.apk path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-arm64-v8a-debug.apk
# - name: Upload artifact phone-libre-armeabi-v7a-debug.apk - name: Upload artifact phone-libre-armeabi-v7a-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: phone-libre-armeabi-v7a-debug.apk name: phone-libre-armeabi-v7a-debug.apk
# path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-armeabi-v7a-debug.apk path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-armeabi-v7a-debug.apk
# - name: Upload artifact phone-libre-x86_64-debug.apk - name: Upload artifact phone-libre-x86_64-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: phone-libre-x86_64-debug.apk name: phone-libre-x86_64-debug.apk
# path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-x86_64-debug.apk path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-x86_64-debug.apk
# - name: Upload artifact phone-libre-x86-debug.apk - name: Upload artifact phone-libre-x86-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: phone-libre-x86-debug.apk name: phone-libre-x86-debug.apk
# path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-x86-debug.apk path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-x86-debug.apk
# - name: Upload artifact tv-libre-arm64-v8a-debug.apk - name: Upload artifact tv-libre-arm64-v8a-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: tv-libre-arm64-v8a-debug.apk name: tv-libre-arm64-v8a-debug.apk
# path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-arm64-v8a-debug.apk path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-arm64-v8a-debug.apk
# - name: Upload artifact tv-libre-armeabi-v7a-debug.apk - name: Upload artifact tv-libre-armeabi-v7a-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: tv-libre-armeabi-v7a-debug.apk name: tv-libre-armeabi-v7a-debug.apk
# path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-armeabi-v7a-debug.apk path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-armeabi-v7a-debug.apk
# - name: Upload artifact tv-libre-x86_64-debug.apk - name: Upload artifact tv-libre-x86_64-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: tv-libre-x86_64-debug.apk name: tv-libre-x86_64-debug.apk
# path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-x86_64-debug.apk path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-x86_64-debug.apk
# - name: Upload artifact tv-libre-x86-debug.apk - name: Upload artifact tv-libre-x86-debug.apk
# uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
# with: with:
# name: tv-libre-x86-debug.apk name: tv-libre-x86-debug.apk
# path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-x86-debug.apk path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-x86-debug.apk

5
.gitignore vendored
View file

@ -10,14 +10,10 @@ local.properties
# Android Studio generated files and folders # Android Studio generated files and folders
captures/ captures/
app/phone/libre/release/
.kotlin
.externalNativeBuild/ .externalNativeBuild/
.cxx/ .cxx/
*.apk *.apk
*.dm
output.json output.json
app/phone/libre/release/output-metadata.json
# IntelliJ # IntelliJ
*.iml *.iml
@ -41,4 +37,3 @@ fastlane/report.xml
fastlane/Preview.html fastlane/Preview.html
fastlane/screenshots fastlane/screenshots
fastlane/test_output fastlane/test_output
push.sh

View file

@ -1,6 +1,7 @@
This privacy policy pertains the Ananas app. This privacy policy pertains the Findroid app.
Ananas does not collect or access any personal information. No identifying information or user data of any kind is made available to third-parties. Findroid does not collect or access any personal information. No identifying information or user data of any kind is made available to third-parties.
This Privacy Policy is effective as of Jun 24th, 2024 and will remain in effect except with respect to any changes in its provisions in the future, which will be in effect immediately after being posted on this page. We reserve the right to update or change our Privacy Policy at any time and you should check this Privacy Policy periodically. Your continued use of the Service after we post any modifications to the Privacy Policy on this page will constitute your acknowledgment of the modifications and your consent to abide and be bound by the modified Privacy Policy. This Privacy Policy is effective as of Feb 8th, 2023 and will remain in effect except with respect to any changes in its provisions in the future, which will be in effect immediately after being posted on this page. We reserve the right to update or change our Privacy Policy at any time and you should check this Privacy Policy periodically. Your continued use of the Service after we post any modifications to the Privacy Policy on this page will constitute your acknowledgment of the modifications and your consent to abide and be bound by the modified Privacy Policy.
Findroid is published by Jarne Demeulemeester. Inquiries can be submitted to jarnedemeulemeester@gmail.com.

View file

@ -1,6 +1,21 @@
![Findroid banner](images/findroid-banner.png)
# Findroid
![GitHub release (with filter)](https://img.shields.io/github/v/release/jarnedemeulemeester/findroid?style=for-the-badge)
![GitHub repo stars](https://img.shields.io/github/stars/jarnedemeulemeester/findroid?style=for-the-badge)
![GitHub issues](https://img.shields.io/github/issues/jarnedemeulemeester/findroid?style=for-the-badge)
![GitHub pull requests](https://img.shields.io/github/issues-pr/jarnedemeulemeester/findroid?style=for-the-badge)
![GitHub all releases](https://img.shields.io/github/downloads/jarnedemeulemeester/findroid/total?style=for-the-badge)
![GitHub](https://img.shields.io/github/license/jarnedemeulemeester/findroid?style=for-the-badge)
Findroid is third-party Android application for Jellyfin that provides a native user interface to browse and play movies and series.
I am developing this application in my spare time.
**This project is in its early stages so expect bugs.**
<a href='https://play.google.com/store/apps/details?id=dev.jdtech.jellyfin'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' height="80"/></a><a href='http://www.amazon.com/gp/product/B0BTWC8DNZ'><img alt='Available at Amazon Appstore' src='https://user-images.githubusercontent.com/32322857/219019331-027a6775-7362-44bb-a026-281f71e9b37b.png' height="80"/></a><a href='https://apt.izzysoft.de/fdroid/index/apk/dev.jdtech.jellyfin'><img alt='Get it on IzzyOnDroid' src='https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png' height="80"/></a>
# Ananas
Personal fork
## Screenshots ## Screenshots
| Home | Library | Movie | Season | Episode | | Home | Library | Movie | Season | Episode |
|-------------------------------------|-------------------------------------|---------------------------------|-----------------------------------|-------------------------------------| |-------------------------------------|-------------------------------------|---------------------------------|-----------------------------------|-------------------------------------|
@ -9,9 +24,8 @@ Personal fork
## Features ## Features
- Completely native interface - Completely native interface
- Supported media items: movies, series, seasons, episodes - Supported media items: movies, series, seasons, episodes
- Direct play and Transcoding - Direct play only, (no transcoding)
- Offline playback / downloads - Offline playback / downloads
- Transcoding Downloads (Original - 720p - 480p - 360p)
- ExoPlayer - ExoPlayer
- Video codecs: H.263, H.264, H.265, VP8, VP9, AV1 - Video codecs: H.263, H.264, H.265, VP8, VP9, AV1
- Support depends on Android device - Support depends on Android device
@ -35,8 +49,20 @@ Personal fork
- Websocket connection (Syncplay) - Websocket connection (Syncplay)
- Chromecast support - Chromecast support
## Translating
[JDTech Weblate](https://weblate.jdtech.dev) is a selfhosted instance of Weblate where you can translate this project and future projects of mine.
## Questions?
[![](https://dcbadge.vercel.app/api/server/tg5VvTFwTV)](https://discord.gg/tg5VvTFwTV)\
We have a Discord server to discuss future development or ask general questions.
## License ## License
This project is licensed under [GPLv3](LICENSE). This project is licensed under [GPLv3](LICENSE).
The logo is a combination of the Jellyfin logo and the Android robot.
The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.
Android is a trademark of Google LLC.
Google Play and the Google Play logo are trademarks of Google LLC.

View file

@ -1,87 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.nomadics9.ananas",
"variantName": "AnanasRelease",
"elements": [
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 16,
"versionName": "0.10.6-0.14.2",
"outputFile": "ananas-v0.10.6-0.14.2-Ananas-armeabi-v7a.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 16,
"versionName": "0.10.6-0.14.2",
"outputFile": "ananas-v0.10.6-0.14.2-Ananas-arm64-v8a.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 16,
"versionName": "0.10.6-0.14.2",
"outputFile": "ananas-v0.10.6-0.14.2-Ananas-x86_64.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 16,
"versionName": "0.10.6-0.14.2",
"outputFile": "ananas-v0.10.6-0.14.2-Ananas-x86.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/ananas-v0.10.6-0.14.2-Ananas-armeabi-v7a.dm",
"baselineProfiles/1/ananas-v0.10.6-0.14.2-Ananas-arm64-v8a.dm",
"baselineProfiles/1/ananas-v0.10.6-0.14.2-Ananas-x86_64.dm",
"baselineProfiles/1/ananas-v0.10.6-0.14.2-Ananas-x86.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/ananas-v0.10.6-0.14.2-Ananas-armeabi-v7a.dm",
"baselineProfiles/0/ananas-v0.10.6-0.14.2-Ananas-arm64-v8a.dm",
"baselineProfiles/0/ananas-v0.10.6-0.14.2-Ananas-x86_64.dm",
"baselineProfiles/0/ananas-v0.10.6-0.14.2-Ananas-x86.dm"
]
}
],
"minSdkVersionForDexing": 28
}

View file

@ -10,23 +10,19 @@ plugins {
} }
android { android {
namespace = "com.nomadics9.ananas" namespace = "dev.jdtech.jellyfin"
compileSdk = Versions.compileSdk compileSdk = Versions.compileSdk
buildToolsVersion = Versions.buildTools buildToolsVersion = Versions.buildTools
defaultConfig { defaultConfig {
applicationId = "com.nomadics9.ananas" applicationId = "dev.jdtech.jellyfin"
minSdk = Versions.minSdk minSdk = Versions.minSdk
targetSdk = Versions.targetSdk targetSdk = Versions.targetSdk
versionCode = Versions.appCode versionCode = Versions.appCode
versionName = Versions.appName versionName = Versions.appName
testInstrumentationRunner = "com.nomadics9.ananas.HiltTestRunner" testInstrumentationRunner = "dev.jdtech.jellyfin.HiltTestRunner"
buildConfigField( "String", "DEFAULT_SERVER_ADDRESS", "\" \"")
buildConfigField( "String", "REQUEST_SERVER_ADDRESS", "\" \"")
buildConfigField("String", "FORGET_PASSWORD_ADDRESS", "\" \"")
buildConfigField("String", "UPDATE_ADDRESS", "\" \"")
} }
applicationVariants.all { applicationVariants.all {
@ -35,7 +31,7 @@ android {
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl } .map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output -> .forEach { output ->
if (variant.buildType.name == "release") { if (variant.buildType.name == "release") {
val outputFileName = "ananas-v${variant.versionName}-${variant.flavorName}-${output.getFilter("ABI")}.apk" val outputFileName = "findroid-v${variant.versionName}-${variant.flavorName}-${output.getFilter("ABI")}.apk"
output.outputFileName = outputFileName output.outputFileName = outputFileName
} }
} }
@ -61,18 +57,10 @@ android {
flavorDimensions += "variant" flavorDimensions += "variant"
productFlavors { productFlavors {
create("libre") { register("libre") {
dimension = "variant" dimension = "variant"
isDefault = true isDefault = true
} }
create("Ananas") {
dimension = "variant"
isDefault = false
buildConfigField( "String", "DEFAULT_SERVER_ADDRESS", "\"https://askar.tv\"")
buildConfigField( "String", "REQUEST_SERVER_ADDRESS", "\"https://r.askar.tv\"")
buildConfigField("String", "FORGET_PASSWORD_ADDRESS", "\"https://user.askar.tv/my/account\"")
buildConfigField("String", "UPDATE_ADDRESS", "\"https://fs.nmd.mov/p/ananas.apk\"")
}
} }
splits { splits {
@ -134,7 +122,6 @@ dependencies {
implementation(libs.material) implementation(libs.material)
implementation(libs.media3.ffmpeg.decoder) implementation(libs.media3.ffmpeg.decoder)
implementation(libs.timber) implementation(libs.timber)
implementation(libs.markwon)
coreLibraryDesugaring(libs.android.desugar.jdk) coreLibraryDesugaring(libs.android.desugar.jdk)

View file

@ -1,87 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.nomadics9.ananas",
"variantName": "libreRelease",
"elements": [
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 11,
"versionName": "0.10.1-0.14.2",
"outputFile": "ananas-v0.10.1-0.14.2-libre-armeabi-v7a.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 11,
"versionName": "0.10.1-0.14.2",
"outputFile": "ananas-v0.10.1-0.14.2-libre-x86_64.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 11,
"versionName": "0.10.1-0.14.2",
"outputFile": "ananas-v0.10.1-0.14.2-libre-arm64-v8a.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 11,
"versionName": "0.10.1-0.14.2",
"outputFile": "ananas-v0.10.1-0.14.2-libre-x86.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/ananas-v0.10.1-0.14.2-libre-armeabi-v7a.dm",
"baselineProfiles/1/ananas-v0.10.1-0.14.2-libre-x86_64.dm",
"baselineProfiles/1/ananas-v0.10.1-0.14.2-libre-arm64-v8a.dm",
"baselineProfiles/1/ananas-v0.10.1-0.14.2-libre-x86.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/ananas-v0.10.1-0.14.2-libre-armeabi-v7a.dm",
"baselineProfiles/0/ananas-v0.10.1-0.14.2-libre-x86_64.dm",
"baselineProfiles/0/ananas-v0.10.1-0.14.2-libre-arm64-v8a.dm",
"baselineProfiles/0/ananas-v0.10.1-0.14.2-libre-x86.dm"
]
}
],
"minSdkVersionForDexing": 28
}

View file

@ -20,16 +20,16 @@
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keepnames class com.nomadics9.ananas.models.PlayerItem -keepnames class dev.jdtech.jellyfin.models.PlayerItem
# ProGuard thinks all SettingsFragments are unused # ProGuard thinks all SettingsFragments are unused
-keep class com.nomadics9.ananas.fragments.SettingsLanguageFragment -keep class dev.jdtech.jellyfin.fragments.SettingsLanguageFragment
-keep class com.nomadics9.ananas.fragments.SettingsAppearanceFragment -keep class dev.jdtech.jellyfin.fragments.SettingsAppearanceFragment
-keep class com.nomadics9.ananas.fragments.SettingsDownloadsFragment -keep class dev.jdtech.jellyfin.fragments.SettingsDownloadsFragment
-keep class com.nomadics9.ananas.fragments.SettingsPlayerFragment -keep class dev.jdtech.jellyfin.fragments.SettingsPlayerFragment
-keep class com.nomadics9.ananas.fragments.SettingsDeviceFragment -keep class dev.jdtech.jellyfin.fragments.SettingsDeviceFragment
-keep class com.nomadics9.ananas.fragments.SettingsCacheFragment -keep class dev.jdtech.jellyfin.fragments.SettingsCacheFragment
-keep class com.nomadics9.ananas.fragments.SettingsNetworkFragment -keep class dev.jdtech.jellyfin.fragments.SettingsNetworkFragment
# These classes are from okhttp and are not used in Android # These classes are from okhttp and are not used in Android
-dontwarn org.bouncycastle.jsse.BCSSLSocket -dontwarn org.bouncycastle.jsse.BCSSLSocket

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.util.Log import android.util.Log
import androidx.hilt.work.HiltWorkerFactory import androidx.hilt.work.HiltWorkerFactory
@ -19,7 +19,7 @@ import androidx.work.testing.WorkManagerTestInitHelper
import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules import dagger.hilt.android.testing.UninstallModules
import com.nomadics9.ananas.di.DatabaseModule import dev.jdtech.jellyfin.di.DatabaseModule
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not import org.hamcrest.CoreMatchers.not
import org.junit.Before import org.junit.Before
@ -76,9 +76,9 @@ class MainActivityTest {
waitForElement(allOf(withText("Movies"), isDisplayed())) waitForElement(allOf(withText("Movies"), isDisplayed()))
onView(withText("Movies")).perform(click()) onView(withText("Movies")).perform(click())
// Navigate to Battle of the Stars // Navigate to The Boy in the Plastic Bubble
waitForElement(allOf(withText("Battle of the Stars"), isDisplayed())) waitForElement(allOf(withText("The Boy in the Plastic Bubble"), isDisplayed()))
onView(withText("Battle of the Stars")).perform(click()) onView(withText("The Boy in the Plastic Bubble")).perform(click())
// Play the movie // Play the movie
waitForElement(allOf(withId(R.id.play_button), isEnabled())) waitForElement(allOf(withId(R.id.play_button), isEnabled()))

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.di package dev.jdtech.jellyfin.di
import android.content.Context import android.content.Context
import androidx.room.Room import androidx.room.Room
@ -7,8 +7,8 @@ import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import com.nomadics9.ananas.database.ServerDatabase import dev.jdtech.jellyfin.database.ServerDatabase
import com.nomadics9.ananas.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module

View file

@ -1,72 +0,0 @@
package com.nomadics9.ananas.fragments
import android.graphics.Bitmap
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ProgressBar
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import com.nomadics9.ananas.BuildConfig
import com.nomadics9.ananas.R
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class RequestsWebViewFragment : Fragment() {
private lateinit var webView: WebView
private lateinit var progressBar: ProgressBar
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_webview, container, false)
webView = rootView.findViewById(R.id.webview)
progressBar = rootView.findViewById(R.id.progressBar)
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true // Enable JavaScript if required
// Set WebViewClient to handle loading URLs within the WebView
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
// Return false to indicate that the WebView should load the URL
return false
}
}
// Set up WebView client to handle page loading events
webView.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
progressBar.visibility = View.VISIBLE // Show progress bar when page starts loading
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
progressBar.visibility = View.GONE // Hide progress bar when page finishes loading
}
}
// Load your URL here
webView.loadUrl(BuildConfig.REQUEST_SERVER_ADDRESS)
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
isEnabled = false
requireActivity().onBackPressed()
}
}
})
return rootView
}
}

View file

@ -1,266 +0,0 @@
package com.nomadics9.ananas.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.EpisodeListAdapter
import com.nomadics9.ananas.databinding.FragmentSeasonBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.getStorageSelectionDialog
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.SeasonEvent
import com.nomadics9.ananas.viewmodels.SeasonViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.models.UiText
import javax.inject.Inject
@AndroidEntryPoint
class SeasonFragment : Fragment() {
private lateinit var binding: FragmentSeasonBinding
private val viewModel: SeasonViewModel by viewModels()
private val args: SeasonFragmentArgs by navArgs()
private lateinit var errorDialog: ErrorDialogFragment
private lateinit var downloadPreparingDialog: AlertDialog
@Inject
lateinit var appPreferences: AppPreferences
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = FragmentSeasonBinding.inflate(inflater, container, false)
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(
object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(com.nomadics9.ananas.core.R.menu.season_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
com.nomadics9.ananas.core.R.id.action_download_season -> {
if (requireContext().getExternalFilesDirs(null).filterNotNull().size > 1) {
val storageDialog = getStorageSelectionDialog(
requireContext(),
onItemSelected = { storageIndex ->
createEpisodesToDownloadDialog(storageIndex)
},
onCancel = {
},
)
viewModel.download()
return true
}
createEpisodesToDownloadDialog()
return true
}
else -> false
}
}
},
viewLifecycleOwner,
Lifecycle.State.RESUMED,
)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.uiState.collect { uiState ->
Timber.d("$uiState")
when (uiState) {
is SeasonViewModel.UiState.Normal -> bindUiStateNormal(uiState)
is SeasonViewModel.UiState.Loading -> bindUiStateLoading()
is SeasonViewModel.UiState.Error -> bindUiStateError(uiState)
}
}
}
launch {
viewModel.downloadStatus.collect { (status, progress) ->
when (status) {
10 -> {
downloadPreparingDialog.dismiss()
}
}
}
}
launch {
viewModel.downloadError.collect { uiText ->
createErrorDialog(uiText)
}
}
launch {
viewModel.eventsChannelFlow.collect { event ->
when (event) {
is SeasonEvent.NavigateBack -> findNavController().navigateUp()
}
}
}
}
}
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
errorDialog.show(parentFragmentManager, ErrorDialogFragment.TAG)
}
binding.episodesRecyclerView.adapter =
EpisodeListAdapter { episode ->
navigateToEpisodeBottomSheetFragment(episode)
}
}
override fun onResume() {
super.onResume()
viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
}
private fun bindUiStateNormal(uiState: SeasonViewModel.UiState.Normal) {
uiState.apply {
val adapter = binding.episodesRecyclerView.adapter as EpisodeListAdapter
adapter.submitList(uiState.episodes)
}
binding.loadingIndicator.isVisible = false
binding.episodesRecyclerView.isVisible = true
binding.errorLayout.errorPanel.isVisible = false
}
private fun bindUiStateLoading() {
binding.loadingIndicator.isVisible = true
binding.errorLayout.errorPanel.isVisible = false
}
private fun bindUiStateError(uiState: SeasonViewModel.UiState.Error) {
errorDialog = ErrorDialogFragment.newInstance(uiState.error)
binding.loadingIndicator.isVisible = false
binding.episodesRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(uiState.error.message)
}
private fun createDownloadPreparingDialog() {
val builder = MaterialAlertDialogBuilder(requireContext())
downloadPreparingDialog = builder
.setTitle(com.nomadics9.ananas.core.R.string.preparing_download)
.setView(com.nomadics9.ananas.R.layout.preparing_download_dialog)
.setCancelable(false)
.create()
downloadPreparingDialog.show()
}
private fun createErrorDialog(uiText: UiText) {
val builder = MaterialAlertDialogBuilder(requireContext())
builder
.setTitle(com.nomadics9.ananas.core.R.string.downloading_error)
.setMessage(uiText.asString(requireContext().resources))
.setPositiveButton(getString(com.nomadics9.ananas.core.R.string.close)) { _, _ ->
}
builder.show()
}
private fun createEpisodesToDownloadDialog(storageIndex: Int = 0) {
if (!appPreferences.downloadQualityDefault)
createPickQualityDialog {
showDownloadDialog(storageIndex)
} else {
showDownloadDialog(storageIndex)
}
}
private fun showDownloadDialog(storageIndex: Int = 0) {
val builder = MaterialAlertDialogBuilder(requireContext())
val dialog = builder
.setTitle(com.nomadics9.ananas.core.R.string.download_season_dialog_title)
.setMessage(com.nomadics9.ananas.core.R.string.download_season_dialog_question)
.setPositiveButton(com.nomadics9.ananas.core.R.string.download_season_dialog_download_all) { _, _ ->
createDownloadPreparingDialog()
viewModel.download(storageIndex = storageIndex, downloadWatched = true)
}
.setNegativeButton(com.nomadics9.ananas.core.R.string.download_season_dialog_download_unwatched) { _, _ ->
createDownloadPreparingDialog()
viewModel.download(storageIndex = storageIndex, downloadWatched = false)
}
.create()
dialog.show()
}
private fun createPickQualityDialog(onQualitySelected: () -> Unit) {
val qualityEntries = resources.getStringArray(com.nomadics9.ananas.core.R.array.download_quality_entries)
val qualityValues = resources.getStringArray(com.nomadics9.ananas.core.R.array.download_quality_values)
val quality = appPreferences.downloadQuality
val currentQualityIndex = qualityValues.indexOf(quality)
var selectedQuality = quality
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setTitle("Download Quality")
builder.setSingleChoiceItems(qualityEntries, currentQualityIndex) { _, which ->
selectedQuality = qualityValues[which]
}
builder.setPositiveButton("Download") { dialog, _ ->
appPreferences.downloadQuality = selectedQuality
onQualitySelected()
dialog.dismiss()
}
builder.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
val dialog = builder.create()
dialog.show()
}
private fun navigateToEpisodeBottomSheetFragment(episode: FindroidEpisode) {
findNavController().navigate(
SeasonFragmentDirections.actionSeasonFragmentToEpisodeBottomSheetFragment(
episode.id,
),
)
}
}

View file

@ -1,175 +0,0 @@
package com.nomadics9.ananas.fragments
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.BuildConfig
import com.nomadics9.ananas.utils.restart
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.net.HttpURLConnection
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
@AndroidEntryPoint
class SettingsFragment : PreferenceFragmentCompat() {
@Inject
lateinit var appPreferences: AppPreferences
private val updateUrl = BuildConfig.UPDATE_ADDRESS
private var isUpdateAvailable: Boolean = false
private var newLastModifiedDate: Date? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(CoreR.xml.fragment_settings, rootKey)
findPreference<Preference>("switchServer")?.setOnPreferenceClickListener {
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionNavigationSettingsToServerSelectFragment())
true
}
findPreference<Preference>("switchUser")?.setOnPreferenceClickListener {
val serverId = appPreferences.currentServer!!
findNavController().navigate(
TwoPaneSettingsFragmentDirections.actionNavigationSettingsToUsersFragment(
serverId
)
)
true
}
findPreference<Preference>("switchAddress")?.setOnPreferenceClickListener {
val serverId = appPreferences.currentServer!!
findNavController().navigate(
TwoPaneSettingsFragmentDirections.actionNavigationSettingsToServerAddressesFragment(
serverId
)
)
true
}
findPreference<Preference>("pref_offline_mode")?.setOnPreferenceClickListener {
activity?.restart()
true
}
findPreference<Preference>("privacyPolicy")?.setOnPreferenceClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://github.com/nomadics9/ananas/blob/main/PRIVACY"),
)
startActivity(intent)
true
}
findPreference<Preference>("appInfo")?.setOnPreferenceClickListener {
if (isUpdateAvailable && newLastModifiedDate != null) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(updateUrl))
startActivity(intent)
storeDate(newLastModifiedDate!!)
true
} else {
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionSettingsFragmentToAboutLibraries())
false
}
}
findPreference<Preference>("requests")?.setOnPreferenceClickListener {
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionNavigationSettingsToRequestsWebFragment())
true
}
// Check for updates when the settings screen is opened
checkForUpdates()
}
private fun checkForUpdates() {
lifecycleScope.launch {
val lastModifiedDate = fetchLastModifiedDate(updateUrl)
if (lastModifiedDate != null) {
Timber.d("Fetched Last-Modified date: $lastModifiedDate")
val storedDate = getStoredDate()
Timber.d("Stored date: $storedDate")
if (storedDate == Date(0L) || lastModifiedDate.after(storedDate)) {
Timber.d("Update available")
isUpdateAvailable = true
newLastModifiedDate = lastModifiedDate
showUpdateAvailable()
} else {
Timber.d("No update available")
isUpdateAvailable = false
}
} else {
Timber.d("Failed to fetch Last-Modified date")
isUpdateAvailable = false
}
}
}
private suspend fun fetchLastModifiedDate(urlString: String): Date? {
return withContext(Dispatchers.IO) {
var urlConnection: HttpURLConnection? = null
try {
val url = URL(urlString)
urlConnection = url.openConnection() as HttpURLConnection
urlConnection.requestMethod = "HEAD"
val lastModified = urlConnection.getHeaderField("Last-Modified")
if (lastModified != null) {
val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US)
dateFormat.parse(lastModified)
} else {
null
}
} catch (e: Exception) {
Timber.e(e, "Error fetching Last-Modified date")
null
} finally {
urlConnection?.disconnect()
}
}
}
private fun getStoredDate(): Date {
val sharedPreferences = preferenceManager.sharedPreferences
val storedDateString = sharedPreferences?.getString("stored_date", null)
Timber.d("Retrieved stored date string: $storedDateString")
return if (storedDateString != null) {
try {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(storedDateString) ?: Date(0)
} catch (e: Exception) {
Timber.e(e, "Error parsing stored date string")
Date(0)
}
} else {
Date(0)
}
}
private fun storeDate(date: Date) {
val dateString = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).format(date)
preferenceManager.sharedPreferences?.edit()?.putString("stored_date", dateString)?.apply()
Timber.d("Stored new date: $dateString")
}
private fun showUpdateAvailable() {
val appInfoPreference = findPreference<Preference>("appInfo")
appInfoPreference?.let {
it.summary = "Update available!"
it.icon = ResourcesCompat.getDrawable(resources, CoreR.drawable.ic_download, null) // Ensure this drawable exists
Timber.d("Update available UI shown")
}
}
}

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.app.Application import android.app.Application
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
@ -14,7 +14,7 @@ import com.google.android.material.color.DynamicColorsOptions
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@HiltAndroidApp @HiltAndroidApp
class BaseApplication : Application(), Configuration.Provider, ImageLoaderFactory { class BaseApplication : Application(), Configuration.Provider, ImageLoaderFactory {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
@ -9,7 +9,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.media3.session.MediaSession import androidx.media3.session.MediaSession
import com.nomadics9.ananas.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
abstract class BasePlayerActivity : AppCompatActivity() { abstract class BasePlayerActivity : AppCompatActivity() {

View file

@ -1,20 +1,20 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import coil.load import coil.load
import com.nomadics9.ananas.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.User import dev.jdtech.jellyfin.models.User
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
import java.util.UUID import java.util.UUID
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
fun bindItemImage(imageView: ImageView, item: BaseItemDto) { fun bindItemImage(imageView: ImageView, item: BaseItemDto) {
val itemId = val itemId =

View file

@ -1,10 +1,12 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraph import androidx.navigation.NavGraph
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
@ -19,16 +21,12 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import com.nomadics9.ananas.databinding.ActivityMainBinding import dev.jdtech.jellyfin.databinding.ActivityMainBinding
import com.nomadics9.ananas.viewmodels.MainViewModel import dev.jdtech.jellyfin.viewmodels.MainViewModel
import com.nomadics9.ananas.work.SyncWorker import dev.jdtech.jellyfin.work.SyncWorker
import com.nomadics9.ananas.repository.JellyfinRepository
import com.nomadics9.ananas.utils.restart
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -40,9 +38,6 @@ class MainActivity : AppCompatActivity() {
@Inject @Inject
lateinit var database: ServerDatabaseDao lateinit var database: ServerDatabaseDao
@Inject
lateinit var jellyfinRepository: JellyfinRepository
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@ -53,6 +48,21 @@ class MainActivity : AppCompatActivity() {
scheduleUserDataSync() scheduleUserDataSync()
applyTheme() applyTheme()
setupActivity() setupActivity()
// Temp fix insets because SDK 35 enables edge to edge by default. This will probably be removed once we move to compose
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val bars = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout(),
)
v.updatePadding(
left = bars.left,
top = bars.top,
right = bars.right,
bottom = bars.bottom,
)
WindowInsetsCompat.CONSUMED
}
} }
@OptIn(NavigationUiSaveStateControl::class) @OptIn(NavigationUiSaveStateControl::class)
@ -76,18 +86,10 @@ class MainActivity : AppCompatActivity() {
val navView: NavigationBarView = binding.navView as NavigationBarView val navView: NavigationBarView = binding.navView as NavigationBarView
if (appPreferences.offlineMode) { if (appPreferences.offlineMode) {
appPreferences.isOffline = true
}
if (appPreferences.isOffline) {
navView.menu.clear() navView.menu.clear()
navView.inflateMenu(CoreR.menu.bottom_nav_menu_offline) navView.inflateMenu(CoreR.menu.bottom_nav_menu_offline)
} }
if (!appPreferences.isOffline && appPreferences.autoOffline) {
testServerConnection()
}
setSupportActionBar(binding.mainToolbar) setSupportActionBar(binding.mainToolbar)
// Passing each menu ID as a set of Ids because each // Passing each menu ID as a set of Ids because each
@ -108,7 +110,7 @@ class MainActivity : AppCompatActivity() {
navController.addOnDestinationChangedListener { _, destination, _ -> navController.addOnDestinationChangedListener { _, destination, _ ->
binding.navView.visibility = when (destination.id) { binding.navView.visibility = when (destination.id) {
R.id.twoPaneSettingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment, com.mikepenz.aboutlibraries.R.id.about_libraries_dest, R.id.usersFragment, R.id.serverAddressesFragment, R.id.requestsWebFragment -> View.GONE R.id.twoPaneSettingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment, com.mikepenz.aboutlibraries.R.id.about_libraries_dest, R.id.usersFragment, R.id.serverAddressesFragment -> View.GONE
else -> View.VISIBLE else -> View.VISIBLE
} }
if (destination.id == com.mikepenz.aboutlibraries.R.id.about_libraries_dest) { if (destination.id == com.mikepenz.aboutlibraries.R.id.about_libraries_dest) {
@ -168,18 +170,4 @@ class MainActivity : AppCompatActivity() {
setTheme(CoreR.style.ThemeOverlay_Findroid_Amoled) setTheme(CoreR.style.ThemeOverlay_Findroid_Amoled)
} }
} }
private fun testServerConnection() {
val activity = this
lifecycleScope.launch {
try {
jellyfinRepository.getPublicSystemInfo()
// Give the UI a chance to load
delay(100)
} catch (e: Exception) {
appPreferences.isOffline = true
activity.restart()
}
}
}
} }

View file

@ -1,5 +1,6 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.annotation.SuppressLint
import android.app.AppOpsManager import android.app.AppOpsManager
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.content.Context import android.content.Context
@ -34,25 +35,25 @@ import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import androidx.navigation.navArgs import androidx.navigation.navArgs
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nomadics9.ananas.databinding.ActivityPlayerBinding
import com.nomadics9.ananas.dialogs.SpeedSelectionDialogFragment
import com.nomadics9.ananas.dialogs.TrackSelectionDialogFragment
import com.nomadics9.ananas.models.FindroidSegment
import com.nomadics9.ananas.utils.PlayerGestureHelper
import com.nomadics9.ananas.utils.PreviewScrubListener
import com.nomadics9.ananas.viewmodels.PlayerActivityViewModel
import com.nomadics9.ananas.viewmodels.PlayerEvents
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment
import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment
import dev.jdtech.jellyfin.models.VideoQuality
import dev.jdtech.jellyfin.utils.PlayerGestureHelper
import dev.jdtech.jellyfin.utils.PreviewScrubListener
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerEvents
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
import com.nomadics9.ananas.models.VideoQuality
var isControlsLocked: Boolean = false var isControlsLocked: Boolean = false
@AndroidEntryPoint @AndroidEntryPoint
class PlayerActivity : BasePlayerActivity() { class PlayerActivity : BasePlayerActivity() {
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@ -61,8 +62,6 @@ class PlayerActivity : BasePlayerActivity() {
override val viewModel: PlayerActivityViewModel by viewModels() override val viewModel: PlayerActivityViewModel by viewModels()
private var previewScrubListener: PreviewScrubListener? = null private var previewScrubListener: PreviewScrubListener? = null
private var wasZoom: Boolean = false private var wasZoom: Boolean = false
private var oldSegment: FindroidSegment? = null
private var buttonPressed: Boolean = false
private val isPipSupported by lazy { private val isPipSupported by lazy {
// Check if device has PiP feature // Check if device has PiP feature
@ -111,13 +110,12 @@ class PlayerActivity : BasePlayerActivity() {
configureInsets(lockedControls) configureInsets(lockedControls)
if (appPreferences.playerGestures) { if (appPreferences.playerGestures) {
playerGestureHelper = playerGestureHelper = PlayerGestureHelper(
PlayerGestureHelper( appPreferences,
appPreferences, this,
this, binding.playerView,
binding.playerView, getSystemService(Context.AUDIO_SERVICE) as AudioManager,
getSystemService(AUDIO_SERVICE) as AudioManager, )
)
} }
binding.playerView.findViewById<View>(R.id.back_button).setOnClickListener { binding.playerView.findViewById<View>(R.id.back_button).setOnClickListener {
@ -129,8 +127,7 @@ class PlayerActivity : BasePlayerActivity() {
val audioButton = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track) val audioButton = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track)
val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle) val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
val speedButton = binding.playerView.findViewById<ImageButton>(R.id.btn_speed) val speedButton = binding.playerView.findViewById<ImageButton>(R.id.btn_speed)
val skipButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro) val skipIntroButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro)
val watchCreditsButton = binding.playerView.findViewById<Button>(R.id.btn_watch_credits)
val pipButton = binding.playerView.findViewById<ImageButton>(R.id.btn_pip) val pipButton = binding.playerView.findViewById<ImageButton>(R.id.btn_pip)
val lockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_lockview) val lockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_lockview)
val unlockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_unlock) val unlockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_unlock)
@ -144,101 +141,19 @@ class PlayerActivity : BasePlayerActivity() {
// Title // Title
videoNameTextView.text = currentItemTitle videoNameTextView.text = currentItemTitle
// Skip Button // Skip Intro button
if (currentSegment != oldSegment) buttonPressed = false skipIntroButton.isVisible = !isInPictureInPictureMode && currentIntro != null
// Button Visibility and Text skipIntroButton.setOnClickListener {
when (currentSegment?.type) { currentIntro?.let {
"intro" -> { binding.playerView.player?.seekTo((it.introEnd * 1000).toLong())
skipButton.text =
getString(CoreR.string.skip_intro_button)
skipButton.isVisible =
!isInPictureInPictureMode &&
!buttonPressed &&
(
showSkip == true ||
(binding.playerView.isControllerFullyVisible && currentSegment?.skip == true)
)
watchCreditsButton.isVisible = false
}
"credit" -> {
skipButton.text =
if (binding.playerView.player?.hasNextMediaItem() == true) {
getString(CoreR.string.skip_credit_button)
} else {
getString(CoreR.string.skip_credit_button_last)
}
skipButton.isVisible =
!isInPictureInPictureMode &&
!buttonPressed &&
currentSegment?.skip == true &&
!binding.playerView.isControllerFullyVisible
watchCreditsButton.isVisible = skipButton.isVisible
}
else -> {
skipButton.isVisible = false
watchCreditsButton.isVisible = false
} }
} }
binding.playerView.setControllerVisibilityListener(
PlayerView.ControllerVisibilityListener { visibility ->
when (currentSegment?.type) {
"intro" -> {
skipButton.isVisible =
!buttonPressed &&
(showSkip == true || (visibility == View.VISIBLE && currentSegment?.skip == true))
}
"credit" -> {
skipButton.isVisible =
!buttonPressed &&
currentSegment?.skip == true &&
visibility == View.GONE
watchCreditsButton.isVisible = skipButton.isVisible
}
}
},
)
// onClick
if (currentSegment?.type == "credit") {
watchCreditsButton.setOnClickListener {
buttonPressed = true
skipButton.isVisible = false
watchCreditsButton.isVisible = false
}
}
skipButton.setOnClickListener {
when (currentSegment?.type) {
"intro" -> {
currentSegment?.let {
binding.playerView.player?.seekTo((it.endTime * 1000).toLong())
}
}
"credit" -> {
if (binding.playerView.player?.hasNextMediaItem() == true) {
binding.playerView.player?.seekToNext()
} else {
finish()
}
}
}
buttonPressed = true
skipButton.isVisible = false
watchCreditsButton.isVisible = false
}
oldSegment = currentSegment
// Trickplay // Trickplay
previewScrubListener?.let { previewScrubListener?.let {
it.currentTrickplay = currentTrickplay it.currentTrickplay = currentTrickplay
} }
playerGestureHelper?.let {
it.currentTrickplay = currentTrickplay
}
// Chapters // Chapters
if (appPreferences.showChapterMarkers && currentChapters != null) { if (appPreferences.showChapterMarkers && currentChapters != null) {
currentChapters?.let { chapters -> currentChapters?.let { chapters ->
@ -276,8 +191,7 @@ class PlayerActivity : BasePlayerActivity() {
if (appPreferences.playerPipGesture) { if (appPreferences.playerPipGesture) {
try { try {
setPictureInPictureParams(pipParams(event.isPlaying)) setPictureInPictureParams(pipParams(event.isPlaying))
} catch (_: IllegalArgumentException) { } catch (_: IllegalArgumentException) { }
}
} }
} }
} }
@ -376,6 +290,7 @@ class PlayerActivity : BasePlayerActivity() {
viewModel.initializePlayer(args.items) viewModel.initializePlayer(args.items)
} }
@SuppressLint("MissingSuperCall")
override fun onUserLeaveHint() { override fun onUserLeaveHint() {
super.onUserLeaveHint() super.onUserLeaveHint()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&
@ -390,38 +305,34 @@ class PlayerActivity : BasePlayerActivity() {
private fun pipParams(enableAutoEnter: Boolean = viewModel.player.isPlaying): PictureInPictureParams { private fun pipParams(enableAutoEnter: Boolean = viewModel.player.isPlaying): PictureInPictureParams {
val displayAspectRatio = Rational(binding.playerView.width, binding.playerView.height) val displayAspectRatio = Rational(binding.playerView.width, binding.playerView.height)
val aspectRatio = val aspectRatio = binding.playerView.player?.videoSize?.let {
binding.playerView.player?.videoSize?.let { Rational(
Rational( it.width.coerceAtMost((it.height * 2.39f).toInt()),
it.width.coerceAtMost((it.height * 2.39f).toInt()), it.height.coerceAtMost((it.width * 2.39f).toInt()),
it.height.coerceAtMost((it.width * 2.39f).toInt()), )
) }
}
val sourceRectHint = val sourceRectHint = if (displayAspectRatio < aspectRatio!!) {
if (displayAspectRatio < aspectRatio!!) { val space = ((binding.playerView.height - (binding.playerView.width.toFloat() / aspectRatio.toFloat())) / 2).toInt()
val space = ((binding.playerView.height - (binding.playerView.width.toFloat() / aspectRatio.toFloat())) / 2).toInt() Rect(
Rect( 0,
0, space,
space, binding.playerView.width,
binding.playerView.width, (binding.playerView.width.toFloat() / aspectRatio.toFloat()).toInt() + space,
(binding.playerView.width.toFloat() / aspectRatio.toFloat()).toInt() + space, )
) } else {
} else { val space = ((binding.playerView.width - (binding.playerView.height.toFloat() * aspectRatio.toFloat())) / 2).toInt()
val space = ((binding.playerView.width - (binding.playerView.height.toFloat() * aspectRatio.toFloat())) / 2).toInt() Rect(
Rect( space,
space, 0,
0, (binding.playerView.height.toFloat() * aspectRatio.toFloat()).toInt() + space,
(binding.playerView.height.toFloat() * aspectRatio.toFloat()).toInt() + space, binding.playerView.height,
binding.playerView.height, )
) }
}
val builder = val builder = PictureInPictureParams.Builder()
PictureInPictureParams .setAspectRatio(aspectRatio)
.Builder() .setSourceRectHint(sourceRectHint)
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setAutoEnterEnabled(enableAutoEnter) builder.setAutoEnterEnabled(enableAutoEnter)
@ -479,29 +390,25 @@ class PlayerActivity : BasePlayerActivity() {
playerGestureHelper?.updateZoomMode(false) playerGestureHelper?.updateZoomMode(false)
// Brightness mode Auto // Brightness mode Auto
window.attributes = window.attributes = window.attributes.apply {
window.attributes.apply { screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE }
}
} }
false -> { false -> {
binding.playerView.useController = true binding.playerView.useController = true
playerGestureHelper?.updateZoomMode(wasZoom) playerGestureHelper?.updateZoomMode(wasZoom)
// Override auto brightness // Override auto brightness
window.attributes = window.attributes = window.attributes.apply {
window.attributes.apply { screenBrightness = if (appPreferences.playerBrightnessRemember) {
screenBrightness = appPreferences.playerBrightness
if (appPreferences.playerBrightnessRemember) { } else {
appPreferences.playerBrightness Settings.System.getInt(
} else { contentResolver,
Settings.System Settings.System.SCREEN_BRIGHTNESS,
.getInt( ).toFloat() / 255
contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
).toFloat() / 255
}
} }
}
} }
} }
} }

View file

@ -1,13 +1,13 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindCardItemImage import dev.jdtech.jellyfin.bindCardItemImage
import com.nomadics9.ananas.databinding.CollectionItemBinding import dev.jdtech.jellyfin.databinding.CollectionItemBinding
import com.nomadics9.ananas.models.FindroidCollection import dev.jdtech.jellyfin.models.FindroidCollection
class CollectionListAdapter( class CollectionListAdapter(
private val onClickListener: (collection: FindroidCollection) -> Unit, private val onClickListener: (collection: FindroidCollection) -> Unit,

View file

@ -1,12 +1,12 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.databinding.DiscoveredServerItemBinding import dev.jdtech.jellyfin.databinding.DiscoveredServerItemBinding
import com.nomadics9.ananas.models.DiscoveredServer import dev.jdtech.jellyfin.models.DiscoveredServer
class DiscoveredServerListAdapter( class DiscoveredServerListAdapter(
private val clickListener: (server: DiscoveredServer) -> Unit, private val clickListener: (server: DiscoveredServer) -> Unit,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.text.Html.fromHtml import android.text.Html.fromHtml
import android.util.TypedValue import android.util.TypedValue
@ -9,15 +9,15 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindCardItemImage import dev.jdtech.jellyfin.bindCardItemImage
import com.nomadics9.ananas.bindItemBackdropById import dev.jdtech.jellyfin.bindItemBackdropById
import com.nomadics9.ananas.bindSeasonPoster import dev.jdtech.jellyfin.bindSeasonPoster
import com.nomadics9.ananas.databinding.EpisodeItemBinding import dev.jdtech.jellyfin.databinding.EpisodeItemBinding
import com.nomadics9.ananas.databinding.SeasonHeaderBinding import dev.jdtech.jellyfin.databinding.SeasonHeaderBinding
import com.nomadics9.ananas.models.EpisodeItem import dev.jdtech.jellyfin.models.EpisodeItem
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
private const val ITEM_VIEW_TYPE_HEADER = 0 private const val ITEM_VIEW_TYPE_HEADER = 0
private const val ITEM_VIEW_TYPE_EPISODE = 1 private const val ITEM_VIEW_TYPE_EPISODE = 1

View file

@ -1,14 +1,14 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.Constants import dev.jdtech.jellyfin.Constants
import com.nomadics9.ananas.databinding.FavoriteSectionBinding import dev.jdtech.jellyfin.databinding.FavoriteSectionBinding
import com.nomadics9.ananas.models.FavoriteSection import dev.jdtech.jellyfin.models.FavoriteSection
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
class FavoritesListAdapter( class FavoritesListAdapter(
private val onItemClickListener: (item: FindroidItem) -> Unit, private val onItemClickListener: (item: FindroidItem) -> Unit,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
@ -8,13 +8,13 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindCardItemImage import dev.jdtech.jellyfin.bindCardItemImage
import com.nomadics9.ananas.databinding.HomeEpisodeItemBinding import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class HomeEpisodeListAdapter(private val onClickListener: (item: FindroidItem) -> Unit) : ListAdapter<FindroidItem, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) { class HomeEpisodeListAdapter(private val onClickListener: (item: FindroidItem) -> Unit) : ListAdapter<FindroidItem, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) {
class EpisodeViewHolder( class EpisodeViewHolder(

View file

@ -1,12 +1,12 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindPersonImage import dev.jdtech.jellyfin.bindPersonImage
import com.nomadics9.ananas.databinding.PersonItemBinding import dev.jdtech.jellyfin.databinding.PersonItemBinding
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
class PersonListAdapter(private val clickListener: (item: BaseItemPerson) -> Unit) : ListAdapter<BaseItemPerson, PersonListAdapter.PersonViewHolder>(DiffCallback) { class PersonListAdapter(private val clickListener: (item: BaseItemPerson) -> Unit) : ListAdapter<BaseItemPerson, PersonListAdapter.PersonViewHolder>(DiffCallback) {

View file

@ -1,12 +1,12 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.databinding.ServerAddressListItemBinding import dev.jdtech.jellyfin.databinding.ServerAddressListItemBinding
import com.nomadics9.ananas.models.ServerAddress import dev.jdtech.jellyfin.models.ServerAddress
class ServerAddressAdapter( class ServerAddressAdapter(
private val clickListener: (address: ServerAddress) -> Unit, private val clickListener: (address: ServerAddress) -> Unit,

View file

@ -1,12 +1,12 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.databinding.ServerItemBinding import dev.jdtech.jellyfin.databinding.ServerItemBinding
import com.nomadics9.ananas.models.Server import dev.jdtech.jellyfin.models.Server
class ServerGridAdapter( class ServerGridAdapter(
private val onClickListener: (server: Server) -> Unit, private val onClickListener: (server: Server) -> Unit,

View file

@ -1,13 +1,13 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindUserImage import dev.jdtech.jellyfin.bindUserImage
import com.nomadics9.ananas.databinding.UserListItemBinding import dev.jdtech.jellyfin.databinding.UserListItemBinding
import com.nomadics9.ananas.models.User import dev.jdtech.jellyfin.models.User
class UserListAdapter( class UserListAdapter(
private val clickListener: (user: User) -> Unit, private val clickListener: (user: User) -> Unit,

View file

@ -1,13 +1,13 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindUserImage import dev.jdtech.jellyfin.bindUserImage
import com.nomadics9.ananas.databinding.UserItemBinding import dev.jdtech.jellyfin.databinding.UserItemBinding
import com.nomadics9.ananas.models.User import dev.jdtech.jellyfin.models.User
class UserLoginListAdapter( class UserLoginListAdapter(
private val clickListener: (user: User) -> Unit, private val clickListener: (user: User) -> Unit,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -7,12 +7,12 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindItemImage import dev.jdtech.jellyfin.bindItemImage
import com.nomadics9.ananas.databinding.BaseItemBinding import dev.jdtech.jellyfin.databinding.BaseItemBinding
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class ViewItemListAdapter( class ViewItemListAdapter(
private val onClickListener: (item: FindroidItem) -> Unit, private val onClickListener: (item: FindroidItem) -> Unit,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -7,12 +7,12 @@ import androidx.core.view.isVisible
import androidx.paging.PagingDataAdapter import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindItemImage import dev.jdtech.jellyfin.bindItemImage
import com.nomadics9.ananas.databinding.BaseItemBinding import dev.jdtech.jellyfin.databinding.BaseItemBinding
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class ViewItemPagingAdapter( class ViewItemPagingAdapter(
private val onClickListener: (item: FindroidItem) -> Unit, private val onClickListener: (item: FindroidItem) -> Unit,

View file

@ -1,17 +1,17 @@
package com.nomadics9.ananas.adapters package dev.jdtech.jellyfin.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.databinding.CardOfflineBinding import dev.jdtech.jellyfin.databinding.CardOfflineBinding
import com.nomadics9.ananas.databinding.NextUpSectionBinding import dev.jdtech.jellyfin.databinding.NextUpSectionBinding
import com.nomadics9.ananas.databinding.ViewItemBinding import dev.jdtech.jellyfin.databinding.ViewItemBinding
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.HomeItem import dev.jdtech.jellyfin.models.HomeItem
import com.nomadics9.ananas.models.View import dev.jdtech.jellyfin.models.View
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
private const val ITEM_VIEW_TYPE_NEXT_UP = 0 private const val ITEM_VIEW_TYPE_NEXT_UP = 0
private const val ITEM_VIEW_TYPE_VIEW = 1 private const val ITEM_VIEW_TYPE_VIEW = 1

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.di package dev.jdtech.jellyfin.di
import android.content.Context import android.content.Context
import dagger.Module import dagger.Module
@ -6,7 +6,7 @@ import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import com.nomadics9.ananas.BaseApplication import dev.jdtech.jellyfin.BaseApplication
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module

View file

@ -1,11 +1,11 @@
package com.nomadics9.ananas.dialogs package dev.jdtech.jellyfin.dialogs
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import android.os.StatFs import android.os.StatFs
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
fun getStorageSelectionDialog( fun getStorageSelectionDialog(
context: Context, context: Context,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
@ -14,12 +14,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.nomadics9.ananas.BuildConfig
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.DiscoveredServerListAdapter import dev.jdtech.jellyfin.adapters.DiscoveredServerListAdapter
import com.nomadics9.ananas.databinding.FragmentAddServerBinding import dev.jdtech.jellyfin.databinding.FragmentAddServerBinding
import com.nomadics9.ananas.viewmodels.AddServerEvent import dev.jdtech.jellyfin.viewmodels.AddServerEvent
import com.nomadics9.ananas.viewmodels.AddServerViewModel import dev.jdtech.jellyfin.viewmodels.AddServerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -90,12 +89,7 @@ class AddServerFragment : Fragment() {
} }
} }
} }
if (BuildConfig.FLAVOR == "Ananas") {
fun connectToServerDirectly(serverAddress: String = BuildConfig.DEFAULT_SERVER_ADDRESS) {
viewModel.checkServer(serverAddress.removeSuffix("/"))
}
connectToServerDirectly()
}
return binding.root return binding.root
} }
@ -140,16 +134,6 @@ class AddServerFragment : Fragment() {
viewModel.checkServer(serverAddress.removeSuffix("/")) viewModel.checkServer(serverAddress.removeSuffix("/"))
} }
// private fun connectToServer() {
// val serverAddress = (binding.editTextServerAddress as AppCompatEditText).text.toString()
// if (serverAddress.isNotBlank()) {
// viewModel.checkServer(serverAddress.removeSuffix("/"))
// } else {
// viewModel.checkServer(BuildConfig.DEFAULT_SERVER_ADDRESS.removeSuffix("/"))
// }
// }
private fun navigateToLoginFragment() { private fun navigateToLoginFragment() {
findNavController().navigate(AddServerFragmentDirections.actionAddServerFragmentToLoginFragment()) findNavController().navigate(AddServerFragmentDirections.actionAddServerFragmentToLoginFragment())
} }

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -13,18 +13,18 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentFavoriteBinding import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.CollectionViewModel import dev.jdtech.jellyfin.viewmodels.CollectionViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class CollectionFragment : Fragment() { class CollectionFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -13,19 +13,19 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentDownloadsBinding import dev.jdtech.jellyfin.databinding.FragmentDownloadsBinding
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.restart import dev.jdtech.jellyfin.utils.restart
import com.nomadics9.ananas.viewmodels.DownloadsEvent import dev.jdtech.jellyfin.viewmodels.DownloadsEvent
import com.nomadics9.ananas.viewmodels.DownloadsViewModel import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class DownloadsFragment : Fragment() { class DownloadsFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.app.DownloadManager import android.app.DownloadManager
import android.os.Bundle import android.os.Bundle
@ -21,23 +21,23 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.R import dev.jdtech.jellyfin.R
import com.nomadics9.ananas.bindCardItemImage import dev.jdtech.jellyfin.bindCardItemImage
import com.nomadics9.ananas.databinding.EpisodeBottomSheetBinding import dev.jdtech.jellyfin.databinding.EpisodeBottomSheetBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.getStorageSelectionDialog import dev.jdtech.jellyfin.dialogs.getStorageSelectionDialog
import com.nomadics9.ananas.dialogs.getVideoVersionDialog import dev.jdtech.jellyfin.dialogs.getVideoVersionDialog
import com.nomadics9.ananas.models.FindroidSourceType import dev.jdtech.jellyfin.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import com.nomadics9.ananas.models.UiText import dev.jdtech.jellyfin.models.UiText
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.models.isDownloading import dev.jdtech.jellyfin.models.isDownloading
import com.nomadics9.ananas.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.EpisodeBottomSheetEvent import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetEvent
import com.nomadics9.ananas.viewmodels.EpisodeBottomSheetViewModel import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.DateTime import org.jellyfin.sdk.model.DateTime
import timber.log.Timber import timber.log.Timber
@ -48,7 +48,7 @@ import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
import android.R as AndroidR import android.R as AndroidR
import com.google.android.material.R as MaterialR import com.google.android.material.R as MaterialR
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class EpisodeBottomSheetFragment : BottomSheetDialogFragment() { class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -12,15 +12,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentFavoriteBinding import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.FavoriteViewModel import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -19,21 +19,21 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.adapters.ViewListAdapter import dev.jdtech.jellyfin.adapters.ViewListAdapter
import com.nomadics9.ananas.databinding.FragmentHomeBinding import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.restart import dev.jdtech.jellyfin.utils.restart
import com.nomadics9.ananas.viewmodels.HomeViewModel import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
@ -74,12 +74,6 @@ class HomeFragment : Fragment() {
val searchView = search.actionView as SearchView val searchView = search.actionView as SearchView
searchView.queryHint = getString(CoreR.string.search_hint) searchView.queryHint = getString(CoreR.string.search_hint)
val requests = menu.findItem(CoreR.id.action_requests)
requests.setOnMenuItemClickListener{
navigateToRequestsWebViewFragment()
true
}
search.setOnActionExpandListener( search.setOnActionExpandListener(
object : MenuItem.OnActionExpandListener { object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean { override fun onMenuItemActionExpand(item: MenuItem): Boolean {
@ -208,7 +202,7 @@ class HomeFragment : Fragment() {
checkIfLoginRequired(uiState.error.message) checkIfLoginRequired(uiState.error.message)
} }
private fun navigateToLibraryFragment(view: com.nomadics9.ananas.models.View) { private fun navigateToLibraryFragment(view: dev.jdtech.jellyfin.models.View) {
findNavController().navigate( findNavController().navigate(
HomeFragmentDirections.actionNavigationHomeToLibraryFragment( HomeFragmentDirections.actionNavigationHomeToLibraryFragment(
libraryId = view.id, libraryId = view.id,
@ -257,10 +251,4 @@ class HomeFragment : Fragment() {
HomeFragmentDirections.actionHomeFragmentToSearchResultFragment(query), HomeFragmentDirections.actionHomeFragmentToSearchResultFragment(query),
) )
} }
private fun navigateToRequestsWebViewFragment() {
findNavController().navigate(
HomeFragmentDirections.actionHomeFragmentToRequestsWebFragment()
)
}
} }

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -19,25 +19,24 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.paging.LoadState import androidx.paging.LoadState
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.adapters.ViewItemPagingAdapter import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
import com.nomadics9.ananas.databinding.FragmentLibraryBinding import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.SortDialogFragment import dev.jdtech.jellyfin.dialogs.SortDialogFragment
import com.nomadics9.ananas.models.FindroidBoxSet import dev.jdtech.jellyfin.models.FindroidBoxSet
import com.nomadics9.ananas.models.FindroidFolder import dev.jdtech.jellyfin.models.FindroidFolder
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.models.SortBy import dev.jdtech.jellyfin.models.SortBy
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.LibraryViewModel import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.SortOrder import org.jellyfin.sdk.model.api.SortOrder
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
import androidx.recyclerview.widget.GridLayoutManager
@AndroidEntryPoint @AndroidEntryPoint
class LibraryFragment : Fragment() { class LibraryFragment : Fragment() {
@ -63,11 +62,6 @@ class LibraryFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.itemsRecyclerView.layoutManager =
GridLayoutManager(context, preferences.spanCount)
val menuHost: MenuHost = requireActivity() val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider( menuHost.addMenuProvider(
object : MenuProvider { object : MenuProvider {

View file

@ -1,7 +1,5 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Html.fromHtml import android.text.Html.fromHtml
import android.view.LayoutInflater import android.view.LayoutInflater
@ -18,18 +16,16 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.BuildConfig import dev.jdtech.jellyfin.adapters.UserLoginListAdapter
import com.nomadics9.ananas.adapters.UserLoginListAdapter import dev.jdtech.jellyfin.database.ServerDatabaseDao
import com.nomadics9.ananas.database.ServerDatabaseDao import dev.jdtech.jellyfin.databinding.FragmentLoginBinding
import com.nomadics9.ananas.databinding.FragmentLoginBinding import dev.jdtech.jellyfin.viewmodels.LoginEvent
import com.nomadics9.ananas.viewmodels.LoginEvent import dev.jdtech.jellyfin.viewmodels.LoginViewModel
import com.nomadics9.ananas.viewmodels.LoginViewModel
import io.noties.markwon.Markwon
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class LoginFragment : Fragment() { class LoginFragment : Fragment() {
@ -82,17 +78,6 @@ class LoginFragment : Fragment() {
(binding.editTextPassword as AppCompatEditText).requestFocus() (binding.editTextPassword as AppCompatEditText).requestFocus()
} }
if (BuildConfig.FLAVOR == "Ananas") {
binding.buttonForgetPassword.setOnClickListener {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.FORGET_PASSWORD_ADDRESS))
startActivity(browserIntent)
}
binding.buttonForgetPassword.isVisible = true
} else {
binding.buttonForgetPassword.isVisible = false
}
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { uiState -> viewModel.uiState.collect { uiState ->
@ -158,21 +143,7 @@ class LoginFragment : Fragment() {
binding.editTextPasswordLayout.isEnabled = true binding.editTextPasswordLayout.isEnabled = true
uiState.disclaimer?.let { disclaimer -> uiState.disclaimer?.let { disclaimer ->
if (BuildConfig.FLAVOR == "Ananas") { binding.loginDisclaimer.text = fromHtml(disclaimer, 0)
val lines = disclaimer.lines()
val lineToRemoveIndex = 3
val filteredLines = lines.toMutableList().apply {
if (size > lineToRemoveIndex) {
removeAt(lineToRemoveIndex)
}
}
val filteredDisclaimer = filteredLines.joinToString("\n")
val markwon = Markwon.create(requireContext())
markwon.setMarkdown(binding.loginDisclaimer, filteredDisclaimer)
} else {
val markwon = Markwon.create(requireContext())
markwon.setMarkdown(binding.loginDisclaimer, disclaimer)
}
} }
} }

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -19,15 +19,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.CollectionListAdapter import dev.jdtech.jellyfin.adapters.CollectionListAdapter
import com.nomadics9.ananas.databinding.FragmentMediaBinding import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidCollection import dev.jdtech.jellyfin.models.FindroidCollection
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.MediaViewModel import dev.jdtech.jellyfin.viewmodels.MediaViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class MediaFragment : Fragment() { class MediaFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.app.DownloadManager import android.app.DownloadManager
import android.content.Intent import android.content.Intent
@ -21,32 +21,32 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.R import dev.jdtech.jellyfin.R
import com.nomadics9.ananas.adapters.PersonListAdapter import dev.jdtech.jellyfin.adapters.PersonListAdapter
import com.nomadics9.ananas.bindItemBackdropImage import dev.jdtech.jellyfin.bindItemBackdropImage
import com.nomadics9.ananas.databinding.FragmentMovieBinding import dev.jdtech.jellyfin.databinding.FragmentMovieBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.getStorageSelectionDialog import dev.jdtech.jellyfin.dialogs.getStorageSelectionDialog
import com.nomadics9.ananas.dialogs.getVideoVersionDialog import dev.jdtech.jellyfin.dialogs.getVideoVersionDialog
import com.nomadics9.ananas.models.AudioCodec import dev.jdtech.jellyfin.models.AudioCodec
import com.nomadics9.ananas.models.DisplayProfile import dev.jdtech.jellyfin.models.DisplayProfile
import com.nomadics9.ananas.models.FindroidSourceType import dev.jdtech.jellyfin.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import com.nomadics9.ananas.models.UiText import dev.jdtech.jellyfin.models.UiText
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.models.isDownloading import dev.jdtech.jellyfin.models.isDownloading
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.MovieEvent import dev.jdtech.jellyfin.viewmodels.MovieEvent
import com.nomadics9.ananas.viewmodels.MovieViewModel import dev.jdtech.jellyfin.viewmodels.MovieViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class MovieFragment : Fragment() { class MovieFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -15,18 +15,18 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import com.nomadics9.ananas.bindItemImage import dev.jdtech.jellyfin.bindItemImage
import com.nomadics9.ananas.databinding.FragmentPersonDetailBinding import dev.jdtech.jellyfin.databinding.FragmentPersonDetailBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.PersonDetailViewModel import dev.jdtech.jellyfin.viewmodels.PersonDetailViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
internal class PersonDetailFragment : Fragment() { internal class PersonDetailFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -13,15 +13,15 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentSearchResultBinding import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.SearchResultViewModel import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber

View file

@ -0,0 +1,120 @@
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.adapters.EpisodeListAdapter
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.SeasonEvent
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
@AndroidEntryPoint
class SeasonFragment : Fragment() {
private lateinit var binding: FragmentSeasonBinding
private val viewModel: SeasonViewModel by viewModels()
private val args: SeasonFragmentArgs by navArgs()
private lateinit var errorDialog: ErrorDialogFragment
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = FragmentSeasonBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.uiState.collect { uiState ->
Timber.d("$uiState")
when (uiState) {
is SeasonViewModel.UiState.Normal -> bindUiStateNormal(uiState)
is SeasonViewModel.UiState.Loading -> bindUiStateLoading()
is SeasonViewModel.UiState.Error -> bindUiStateError(uiState)
}
}
}
launch {
viewModel.eventsChannelFlow.collect { event ->
when (event) {
is SeasonEvent.NavigateBack -> findNavController().navigateUp()
}
}
}
}
}
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
errorDialog.show(parentFragmentManager, ErrorDialogFragment.TAG)
}
binding.episodesRecyclerView.adapter =
EpisodeListAdapter { episode ->
navigateToEpisodeBottomSheetFragment(episode)
}
}
override fun onResume() {
super.onResume()
viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
}
private fun bindUiStateNormal(uiState: SeasonViewModel.UiState.Normal) {
uiState.apply {
val adapter = binding.episodesRecyclerView.adapter as EpisodeListAdapter
adapter.submitList(uiState.episodes)
}
binding.loadingIndicator.isVisible = false
binding.episodesRecyclerView.isVisible = true
binding.errorLayout.errorPanel.isVisible = false
}
private fun bindUiStateLoading() {
binding.loadingIndicator.isVisible = true
binding.errorLayout.errorPanel.isVisible = false
}
private fun bindUiStateError(uiState: SeasonViewModel.UiState.Error) {
errorDialog = ErrorDialogFragment.newInstance(uiState.error)
binding.loadingIndicator.isVisible = false
binding.episodesRecyclerView.isVisible = false
binding.errorLayout.errorPanel.isVisible = true
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToEpisodeBottomSheetFragment(episode: FindroidEpisode) {
findNavController().navigate(
SeasonFragmentDirections.actionSeasonFragmentToEpisodeBottomSheetFragment(
episode.id,
),
)
}
}

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -12,12 +12,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ServerAddressAdapter import dev.jdtech.jellyfin.adapters.ServerAddressAdapter
import com.nomadics9.ananas.databinding.FragmentServerAddressesBinding import dev.jdtech.jellyfin.databinding.FragmentServerAddressesBinding
import com.nomadics9.ananas.dialogs.AddServerAddressDialog import dev.jdtech.jellyfin.dialogs.AddServerAddressDialog
import com.nomadics9.ananas.dialogs.DeleteServerAddressDialog import dev.jdtech.jellyfin.dialogs.DeleteServerAddressDialog
import com.nomadics9.ananas.viewmodels.ServerAddressesEvent import dev.jdtech.jellyfin.viewmodels.ServerAddressesEvent
import com.nomadics9.ananas.viewmodels.ServerAddressesViewModel import dev.jdtech.jellyfin.viewmodels.ServerAddressesViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -11,11 +11,11 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter
import com.nomadics9.ananas.databinding.FragmentServerSelectBinding import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
import com.nomadics9.ananas.dialogs.DeleteServerDialogFragment import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
import com.nomadics9.ananas.viewmodels.ServerSelectEvent import dev.jdtech.jellyfin.viewmodels.ServerSelectEvent
import com.nomadics9.ananas.viewmodels.ServerSelectViewModel import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber

View file

@ -1,11 +1,11 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsAppearanceFragment : PreferenceFragmentCompat() { class SettingsAppearanceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,10 +1,10 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsCacheFragment : PreferenceFragmentCompat() { class SettingsCacheFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,12 +1,12 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.viewmodels.SettingsDeviceViewModel import dev.jdtech.jellyfin.viewmodels.SettingsDeviceViewModel
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class SettingsDeviceFragment : PreferenceFragmentCompat() { class SettingsDeviceFragment : PreferenceFragmentCompat() {

View file

@ -1,8 +1,8 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsDownloadsFragment : PreferenceFragmentCompat() { class SettingsDownloadsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -0,0 +1,59 @@
package dev.jdtech.jellyfin.fragments
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.utils.restart
import javax.inject.Inject
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class SettingsFragment : PreferenceFragmentCompat() {
@Inject
lateinit var appPreferences: AppPreferences
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(CoreR.xml.fragment_settings, rootKey)
findPreference<Preference>("switchServer")?.setOnPreferenceClickListener {
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionNavigationSettingsToServerSelectFragment())
true
}
findPreference<Preference>("switchUser")?.setOnPreferenceClickListener {
val serverId = appPreferences.currentServer!!
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionNavigationSettingsToUsersFragment(serverId))
true
}
findPreference<Preference>("switchAddress")?.setOnPreferenceClickListener {
val serverId = appPreferences.currentServer!!
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionNavigationSettingsToServerAddressesFragment(serverId))
true
}
findPreference<Preference>("pref_offline_mode")?.setOnPreferenceClickListener {
activity?.restart()
true
}
findPreference<Preference>("privacyPolicy")?.setOnPreferenceClickListener {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://github.com/jarnedemeulemeester/findroid/blob/main/PRIVACY"),
)
startActivity(intent)
true
}
findPreference<Preference>("appInfo")?.setOnPreferenceClickListener {
findNavController().navigate(TwoPaneSettingsFragmentDirections.actionSettingsFragmentToAboutLibraries())
true
}
}
}

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -7,7 +7,7 @@ import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsLanguageFragment : PreferenceFragmentCompat() { class SettingsLanguageFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,11 +1,11 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.Constants import dev.jdtech.jellyfin.Constants
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsNetworkFragment : PreferenceFragmentCompat() { class SettingsNetworkFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -7,7 +7,7 @@ import android.text.InputType
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
class SettingsPlayerFragment : PreferenceFragmentCompat() { class SettingsPlayerFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -18,29 +18,29 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.R import com.google.android.material.R
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.adapters.PersonListAdapter import dev.jdtech.jellyfin.adapters.PersonListAdapter
import com.nomadics9.ananas.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import com.nomadics9.ananas.bindCardItemImage import dev.jdtech.jellyfin.bindCardItemImage
import com.nomadics9.ananas.bindItemBackdropImage import dev.jdtech.jellyfin.bindItemBackdropImage
import com.nomadics9.ananas.databinding.FragmentShowBinding import dev.jdtech.jellyfin.databinding.FragmentShowBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidSeason import dev.jdtech.jellyfin.models.FindroidSeason
import com.nomadics9.ananas.models.FindroidSourceType import dev.jdtech.jellyfin.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import com.nomadics9.ananas.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloaded
import com.nomadics9.ananas.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import com.nomadics9.ananas.viewmodels.ShowEvent import dev.jdtech.jellyfin.viewmodels.ShowEvent
import com.nomadics9.ananas.viewmodels.ShowViewModel import dev.jdtech.jellyfin.viewmodels.ShowViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint @AndroidEntryPoint
class ShowFragment : Fragment() { class ShowFragment : Fragment() {

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceHeaderFragmentCompat import androidx.preference.PreferenceHeaderFragmentCompat

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -12,12 +12,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppNavigationDirections import dev.jdtech.jellyfin.AppNavigationDirections
import com.nomadics9.ananas.adapters.UserListAdapter import dev.jdtech.jellyfin.adapters.UserListAdapter
import com.nomadics9.ananas.databinding.FragmentUsersBinding import dev.jdtech.jellyfin.databinding.FragmentUsersBinding
import com.nomadics9.ananas.dialogs.DeleteUserDialogFragment import dev.jdtech.jellyfin.dialogs.DeleteUserDialogFragment
import com.nomadics9.ananas.viewmodels.UsersEvent import dev.jdtech.jellyfin.viewmodels.UsersEvent
import com.nomadics9.ananas.viewmodels.UsersViewModel import dev.jdtech.jellyfin.viewmodels.UsersViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber

View file

@ -1,8 +1,8 @@
package com.nomadics9.ananas.utils package dev.jdtech.jellyfin.utils
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.nomadics9.ananas.AppNavigationDirections import dev.jdtech.jellyfin.AppNavigationDirections
import timber.log.Timber import timber.log.Timber
fun Fragment.checkIfLoginRequired(error: String?) { fun Fragment.checkIfLoginRequired(error: String?) {

View file

@ -1,8 +1,7 @@
package com.nomadics9.ananas.utils package dev.jdtech.jellyfin.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Bitmap
import android.media.AudioManager import android.media.AudioManager
import android.os.Build import android.os.Build
import android.os.SystemClock import android.os.SystemClock
@ -20,20 +19,15 @@ import android.view.animation.DecelerateInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import coil.transform.RoundedCornersTransformation import dev.jdtech.jellyfin.AppPreferences
import com.nomadics9.ananas.AppPreferences import dev.jdtech.jellyfin.Constants
import com.nomadics9.ananas.Constants import dev.jdtech.jellyfin.PlayerActivity
import com.nomadics9.ananas.PlayerActivity import dev.jdtech.jellyfin.isControlsLocked
import com.nomadics9.ananas.isControlsLocked import dev.jdtech.jellyfin.models.PlayerChapter
import com.nomadics9.ananas.models.PlayerChapter import dev.jdtech.jellyfin.mpv.MPVPlayer
import com.nomadics9.ananas.models.Trickplay
import com.nomadics9.ananas.mpv.MPVPlayer
import timber.log.Timber import timber.log.Timber
import kotlin.math.abs import kotlin.math.abs
import kotlinx.coroutines.Dispatchers
import coil.load
class PlayerGestureHelper( class PlayerGestureHelper(
private val appPreferences: AppPreferences, private val appPreferences: AppPreferences,
private val activity: PlayerActivity, private val activity: PlayerActivity,
@ -68,10 +62,6 @@ class PlayerGestureHelper(
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private val screenHeight = Resources.getSystem().displayMetrics.heightPixels private val screenHeight = Resources.getSystem().displayMetrics.heightPixels
var currentTrickplay: Trickplay? = null
private val roundedCorners = RoundedCornersTransformation(10f)
private var currentBitMap: Bitmap? = null
private var currentNumberOfPointers: Int = 0 private var currentNumberOfPointers: Int = 0
private val tapGestureDetector = GestureDetector( private val tapGestureDetector = GestureDetector(
@ -275,13 +265,6 @@ class PlayerGestureHelper(
activity.binding.progressScrubberLayout.visibility = View.VISIBLE activity.binding.progressScrubberLayout.visibility = View.VISIBLE
activity.binding.progressScrubberText.text = "${longToTimestamp(difference)} [${longToTimestamp(newPos, true)}]" activity.binding.progressScrubberText.text = "${longToTimestamp(difference)} [${longToTimestamp(newPos, true)}]"
swipeGestureValueTrackerProgress = newPos swipeGestureValueTrackerProgress = newPos
if (currentTrickplay != null) {
onMove(newPos)
} else {
activity.binding.imagePreviewGesture.visibility = View.GONE
}
swipeGestureProgressOpen = true swipeGestureProgressOpen = true
true true
} else { } else {
@ -488,28 +471,11 @@ class PlayerGestureHelper(
return false return false
} }
fun onMove(position: Long) {
val trickplay = currentTrickplay ?: return
val image = trickplay.images[position.div(trickplay.interval).toInt()]
if (currentBitMap != image) {
activity.binding.imagePreviewGesture.load(image) {
dispatcher(Dispatchers.Main.immediate)
transformations(roundedCorners)
}
currentBitMap = image
}
}
init { init {
if (appPreferences.playerBrightnessRemember) { if (appPreferences.playerBrightnessRemember) {
activity.window.attributes.screenBrightness = appPreferences.playerBrightness activity.window.attributes.screenBrightness = appPreferences.playerBrightness
} }
if (!appPreferences.playerTrickPlayGesture) {
activity.binding.imagePreviewGesture.visibility = View.GONE
}
updateZoomMode(appPreferences.playerStartMaximized) updateZoomMode(appPreferences.playerStartMaximized)
@Suppress("ClickableViewAccessibility") @Suppress("ClickableViewAccessibility")

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.utils package dev.jdtech.jellyfin.utils
import android.graphics.Bitmap import android.graphics.Bitmap
import android.view.View import android.view.View
@ -8,7 +8,7 @@ import androidx.media3.common.Player
import androidx.media3.ui.TimeBar import androidx.media3.ui.TimeBar
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import com.nomadics9.ananas.models.Trickplay import dev.jdtech.jellyfin.models.Trickplay
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import timber.log.Timber import timber.log.Timber

View file

@ -37,16 +37,7 @@
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/main_toolbar" android:id="@+id/main_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"> android:layout_height="?attr/actionBarSize" />
<ImageView
android:layout_width="65dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="@string/app_name"
android:src="@drawable/ic_launcher_foreground" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -40,17 +39,8 @@
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/main_toolbar" android:id="@+id/main_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"> android:layout_height="?attr/actionBarSize" />
<ImageView
android:layout_width="65dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="@string/app_name"
android:src="@drawable/ic_launcher_foreground" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -16,11 +16,8 @@
<LinearLayout <LinearLayout
android:id="@+id/progress_scrubber_layout" android:id="@+id/progress_scrubber_layout"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginTop="64dp"
android:padding="10dp"
android:background="@drawable/overlay_background" android:background="@drawable/overlay_background"
android:clickable="false" android:clickable="false"
android:gravity="center" android:gravity="center"
@ -28,20 +25,10 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">
<ImageView
android:id="@+id/image_preview_gesture"
android:layout_width="272dp"
android:layout_height="153dp"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:contentDescription="@string/player_trickplay" />
<TextView <TextView
android:id="@+id/progress_scrubber_text" android:id="@+id/progress_scrubber_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall" android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
android:textColor="@android:color/white" android:textColor="@android:color/white"
tools:text="+00:00:10 [00:00:20]" /> tools:text="+00:00:10 [00:00:20]" />

View file

@ -10,7 +10,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center"> android:layout_gravity="center">
<!-- Video surface will be inserted as the first child of the content frame. --> <!-- Video surface will be inserted as the first child of the content frame. -->
<View <View
@ -49,6 +48,24 @@
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="14sp" /> android:textSize="14sp" />
<Button
android:id="@+id/btn_skip_intro"
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginEnd="24dp"
android:layout_marginBottom="64dp"
android:text="@string/player_controls_skip_intro"
android:textColor="@android:color/white"
android:visibility="gone"
app:backgroundTint="@color/player_background"
app:icon="@drawable/ic_skip_forward"
app:iconGravity="end"
app:iconTint="@android:color/white"
app:strokeColor="@android:color/white"
tools:visibility="visible" />
</androidx.media3.ui.AspectRatioFrameLayout> </androidx.media3.ui.AspectRatioFrameLayout>
<androidx.media3.ui.SubtitleView <androidx.media3.ui.SubtitleView
@ -72,33 +89,4 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:animation_enabled="false"/> app:animation_enabled="false"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="end|bottom"
android:layout_marginEnd="24dp"
android:layout_marginBottom="64dp">
<Button
android:id="@+id/btn_watch_credits"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="@string/watch_credits"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/btn_skip_intro"
style="@style/Widget.Material3.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:icon="@drawable/ic_skip_forward"
tools:visibility="visible" />
</LinearLayout>
</merge> </merge>

View file

@ -141,24 +141,10 @@
android:visibility="invisible" /> android:visibility="invisible" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp">
<Button
android:id="@+id/button_forget_password"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/forget_password" />
</RelativeLayout>
<TextView <TextView
android:id="@+id/login_disclaimer" android:id="@+id/login_disclaimer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginHorizontal="24dp" android:layout_marginHorizontal="24dp"
android:layout_margin="24dp" android:layout_margin="24dp"
android:textSize="16sp" android:textSize="16sp"

View file

@ -1,16 +0,0 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>

View file

@ -7,7 +7,7 @@
<fragment <fragment
android:id="@+id/homeFragment" android:id="@+id/homeFragment"
android:name="com.nomadics9.ananas.fragments.HomeFragment" android:name="dev.jdtech.jellyfin.fragments.HomeFragment"
android:label="@string/title_home" android:label="@string/title_home"
tools:layout="@layout/fragment_home"> tools:layout="@layout/fragment_home">
<action <action
@ -49,14 +49,11 @@
<action <action
android:id="@+id/action_homeFragment_to_searchResultFragment" android:id="@+id/action_homeFragment_to_searchResultFragment"
app:destination="@id/searchResultFragment" /> app:destination="@id/searchResultFragment" />
<action
android:id="@+id/action_homeFragment_to_requestsWebFragment"
app:destination="@id/requestsWebFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/mediaFragment" android:id="@+id/mediaFragment"
android:name="com.nomadics9.ananas.fragments.MediaFragment" android:name="dev.jdtech.jellyfin.fragments.MediaFragment"
android:label="@string/title_media" android:label="@string/title_media"
tools:layout="@layout/fragment_media"> tools:layout="@layout/fragment_media">
<action <action
@ -73,7 +70,7 @@
<fragment <fragment
android:id="@+id/twoPaneSettingsFragment" android:id="@+id/twoPaneSettingsFragment"
android:name="com.nomadics9.ananas.fragments.TwoPaneSettingsFragment" android:name="dev.jdtech.jellyfin.fragments.TwoPaneSettingsFragment"
android:label="@string/title_settings"> android:label="@string/title_settings">
<action <action
android:id="@+id/action_navigation_settings_to_serverSelectFragment" android:id="@+id/action_navigation_settings_to_serverSelectFragment"
@ -87,23 +84,13 @@
<action <action
android:id="@+id/action_settingsFragment_to_about_libraries" android:id="@+id/action_settingsFragment_to_about_libraries"
app:destination="@id/about_libraries" /> app:destination="@id/about_libraries" />
<action
android:id="@+id/action_navigation_settings_to_requestsWebFragment"
app:destination="@id/requestsWebFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/settingsFragment" android:id="@+id/settingsFragment"
android:name="com.nomadics9.ananas.fragments.SettingsFragment"> android:name="dev.jdtech.jellyfin.fragments.SettingsFragment" />
</fragment>
<fragment
android:id="@+id/requestsWebFragment"
android:name="com.nomadics9.ananas.fragments.RequestsWebViewFragment"
android:label="Requests"
tools:layout="@layout/fragment_webview">
</fragment>
<fragment <fragment
android:id="@+id/libraryFragment" android:id="@+id/libraryFragment"
android:name="com.nomadics9.ananas.fragments.LibraryFragment" android:name="dev.jdtech.jellyfin.fragments.LibraryFragment"
android:label="{libraryName}" android:label="{libraryName}"
tools:layout="@layout/fragment_library"> tools:layout="@layout/fragment_library">
<argument <argument
@ -135,14 +122,14 @@
app:popExitAnim="@anim/nav_default_pop_exit_anim" /> app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<argument <argument
android:name="libraryType" android:name="libraryType"
app:argType="com.nomadics9.ananas.models.CollectionType" /> app:argType="dev.jdtech.jellyfin.models.CollectionType" />
<action <action
android:id="@+id/action_libraryFragment_self" android:id="@+id/action_libraryFragment_self"
app:destination="@id/libraryFragment" /> app:destination="@id/libraryFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/showFragment" android:id="@+id/showFragment"
android:name="com.nomadics9.ananas.fragments.ShowFragment" android:name="dev.jdtech.jellyfin.fragments.ShowFragment"
android:label="{itemName}" android:label="{itemName}"
tools:layout="@layout/fragment_show"> tools:layout="@layout/fragment_show">
<argument <argument
@ -171,7 +158,7 @@
<fragment <fragment
android:id="@+id/movieFragment" android:id="@+id/movieFragment"
android:name="com.nomadics9.ananas.fragments.MovieFragment" android:name="dev.jdtech.jellyfin.fragments.MovieFragment"
android:label="{itemName}" android:label="{itemName}"
tools:layout="@layout/fragment_movie"> tools:layout="@layout/fragment_movie">
<argument <argument
@ -192,7 +179,7 @@
<fragment <fragment
android:id="@+id/seasonFragment" android:id="@+id/seasonFragment"
android:name="com.nomadics9.ananas.fragments.SeasonFragment" android:name="dev.jdtech.jellyfin.fragments.SeasonFragment"
android:label="{seasonName}" android:label="{seasonName}"
tools:layout="@layout/fragment_season"> tools:layout="@layout/fragment_season">
<argument <argument
@ -224,7 +211,7 @@
</fragment> </fragment>
<dialog <dialog
android:id="@+id/episodeBottomSheetFragment" android:id="@+id/episodeBottomSheetFragment"
android:name="com.nomadics9.ananas.fragments.EpisodeBottomSheetFragment" android:name="dev.jdtech.jellyfin.fragments.EpisodeBottomSheetFragment"
android:label="EpisodeBottomSheetFragment" android:label="EpisodeBottomSheetFragment"
tools:layout="@layout/episode_bottom_sheet"> tools:layout="@layout/episode_bottom_sheet">
<argument <argument
@ -239,7 +226,7 @@
</dialog> </dialog>
<fragment <fragment
android:id="@+id/favoriteFragment" android:id="@+id/favoriteFragment"
android:name="com.nomadics9.ananas.fragments.FavoriteFragment" android:name="dev.jdtech.jellyfin.fragments.FavoriteFragment"
android:label="@string/title_favorite" android:label="@string/title_favorite"
tools:layout="@layout/fragment_favorite"> tools:layout="@layout/fragment_favorite">
<action <action
@ -254,7 +241,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/collectionFragment" android:id="@+id/collectionFragment"
android:name="com.nomadics9.ananas.fragments.CollectionFragment" android:name="dev.jdtech.jellyfin.fragments.CollectionFragment"
android:label="{collectionName}" android:label="{collectionName}"
tools:layout="@layout/fragment_favorite"> tools:layout="@layout/fragment_favorite">
<argument <argument
@ -277,7 +264,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/searchResultFragment" android:id="@+id/searchResultFragment"
android:name="com.nomadics9.ananas.fragments.SearchResultFragment" android:name="dev.jdtech.jellyfin.fragments.SearchResultFragment"
android:label="{query}" android:label="{query}"
tools:layout="@layout/fragment_search_result"> tools:layout="@layout/fragment_search_result">
<action <action
@ -295,7 +282,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/addServerFragment" android:id="@+id/addServerFragment"
android:name="com.nomadics9.ananas.fragments.AddServerFragment" android:name="dev.jdtech.jellyfin.fragments.AddServerFragment"
android:label="@string/add_server" android:label="@string/add_server"
tools:layout="@layout/fragment_add_server"> tools:layout="@layout/fragment_add_server">
<action <action
@ -304,7 +291,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/serverSelectFragment" android:id="@+id/serverSelectFragment"
android:name="com.nomadics9.ananas.fragments.ServerSelectFragment" android:name="dev.jdtech.jellyfin.fragments.ServerSelectFragment"
android:label="@string/select_server" android:label="@string/select_server"
tools:layout="@layout/fragment_server_select"> tools:layout="@layout/fragment_server_select">
<action <action
@ -321,7 +308,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/loginFragment" android:id="@+id/loginFragment"
android:name="com.nomadics9.ananas.fragments.LoginFragment" android:name="dev.jdtech.jellyfin.fragments.LoginFragment"
android:label="@string/login" android:label="@string/login"
tools:layout="@layout/fragment_login"> tools:layout="@layout/fragment_login">
<action <action
@ -337,7 +324,7 @@
<fragment <fragment
android:id="@+id/personDetailFragment" android:id="@+id/personDetailFragment"
android:name="com.nomadics9.ananas.fragments.PersonDetailFragment" android:name="dev.jdtech.jellyfin.fragments.PersonDetailFragment"
android:label="@string/person_detail_title" android:label="@string/person_detail_title"
tools:layout="@layout/fragment_person_detail"> tools:layout="@layout/fragment_person_detail">
@ -355,12 +342,12 @@
<activity <activity
android:id="@+id/playerActivity" android:id="@+id/playerActivity"
android:name="com.nomadics9.ananas.PlayerActivity" android:name="dev.jdtech.jellyfin.PlayerActivity"
android:label="activity_player" android:label="activity_player"
tools:layout="@layout/activity_player"> tools:layout="@layout/activity_player">
<argument <argument
android:name="items" android:name="items"
app:argType="com.nomadics9.ananas.models.PlayerItem[]" /> app:argType="dev.jdtech.jellyfin.models.PlayerItem[]" />
</activity> </activity>
<include app:graph="@navigation/aboutlibs_navigation" /> <include app:graph="@navigation/aboutlibs_navigation" />
@ -370,7 +357,7 @@
<fragment <fragment
android:id="@+id/usersFragment" android:id="@+id/usersFragment"
android:name="com.nomadics9.ananas.fragments.UsersFragment" android:name="dev.jdtech.jellyfin.fragments.UsersFragment"
android:label="@string/users" android:label="@string/users"
tools:layout="@layout/fragment_users"> tools:layout="@layout/fragment_users">
<action <action
@ -388,7 +375,7 @@
<fragment <fragment
android:id="@+id/serverAddressesFragment" android:id="@+id/serverAddressesFragment"
android:name="com.nomadics9.ananas.fragments.ServerAddressesFragment" android:name="dev.jdtech.jellyfin.fragments.ServerAddressesFragment"
android:label="@string/addresses" android:label="@string/addresses"
tools:layout="@layout/fragment_server_addresses"> tools:layout="@layout/fragment_server_addresses">
<action <action
@ -403,7 +390,7 @@
<fragment <fragment
android:id="@+id/downloadsFragment" android:id="@+id/downloadsFragment"
android:name="com.nomadics9.ananas.fragments.DownloadsFragment" android:name="dev.jdtech.jellyfin.fragments.DownloadsFragment"
android:label="@string/title_download" android:label="@string/title_download"
tools:layout="@layout/fragment_favorite"> tools:layout="@layout/fragment_favorite">
<action <action

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="watch_credits">Watch credits</string>
</resources>

View file

@ -9,12 +9,12 @@ plugins {
} }
android { android {
namespace = "com.nomadics9.ananas" namespace = "dev.jdtech.jellyfin"
compileSdk = Versions.compileSdk compileSdk = Versions.compileSdk
buildToolsVersion = Versions.buildTools buildToolsVersion = Versions.buildTools
defaultConfig { defaultConfig {
applicationId = "com.nomadics9.ananas" applicationId = "dev.jdtech.jellyfin"
minSdk = Versions.minSdk minSdk = Versions.minSdk
targetSdk = Versions.targetSdk targetSdk = Versions.targetSdk
@ -81,15 +81,14 @@ ktlint {
} }
dependencies { dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(projects.core) implementation(projects.core)
implementation(projects.data) implementation(projects.data)
implementation(projects.preferences) implementation(projects.preferences)
implementation(projects.player.core) implementation(projects.player.core)
implementation(projects.player.video) implementation(projects.player.video)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(composeBom) implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material3)
implementation(libs.androidx.core) implementation(libs.androidx.core)
@ -99,7 +98,6 @@ dependencies {
implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.ui)
implementation(libs.androidx.media3.session) implementation(libs.androidx.media3.session)
implementation(libs.androidx.paging.compose) implementation(libs.androidx.paging.compose)
implementation(libs.androidx.tv.foundation)
implementation(libs.androidx.tv.material) implementation(libs.androidx.tv.material)
implementation(libs.coil.compose) implementation(libs.coil.compose)
implementation(libs.coil.svg) implementation(libs.coil.svg)

View file

@ -1,65 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.nomadics9.ananas.debug",
"variantName": "libreDebug",
"elements": [
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-armeabi-v7a-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86_64-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-arm64-v8a-debug.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86-debug.apk"
}
],
"elementType": "File",
"minSdkVersionForDexing": 28
}

View file

@ -1,87 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.nomadics9.ananas",
"variantName": "libreRelease",
"elements": [
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-armeabi-v7a-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-arm64-v8a-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 6,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86_64-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/tv-libre-armeabi-v7a-release.dm",
"baselineProfiles/1/tv-libre-x86-release.dm",
"baselineProfiles/1/tv-libre-arm64-v8a-release.dm",
"baselineProfiles/1/tv-libre-x86_64-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/tv-libre-armeabi-v7a-release.dm",
"baselineProfiles/0/tv-libre-x86-release.dm",
"baselineProfiles/0/tv-libre-arm64-v8a-release.dm",
"baselineProfiles/0/tv-libre-x86_64-release.dm"
]
}
],
"minSdkVersionForDexing": 28
}

View file

@ -1,87 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.nomadics9.ananas.staging",
"variantName": "libreStaging",
"elements": [
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "armeabi-v7a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "0.14.2",
"outputFile": "tv-libre-armeabi-v7a-staging.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86_64-staging.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "0.14.2",
"outputFile": "tv-libre-x86-staging.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 1,
"versionName": "0.14.2",
"outputFile": "tv-libre-arm64-v8a-staging.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/tv-libre-armeabi-v7a-staging.dm",
"baselineProfiles/1/tv-libre-x86_64-staging.dm",
"baselineProfiles/1/tv-libre-x86-staging.dm",
"baselineProfiles/1/tv-libre-arm64-v8a-staging.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/tv-libre-armeabi-v7a-staging.dm",
"baselineProfiles/0/tv-libre-x86_64-staging.dm",
"baselineProfiles/0/tv-libre-x86-staging.dm",
"baselineProfiles/0/tv-libre-arm64-v8a-staging.dm"
]
}
],
"minSdkVersionForDexing": 28
}

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.app.Application import android.app.Application
import coil.ImageLoader import coil.ImageLoader

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@ -6,11 +6,11 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.DestinationsNavHost
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.database.ServerDatabaseDao import dev.jdtech.jellyfin.database.ServerDatabaseDao
import com.nomadics9.ananas.destinations.AddServerScreenDestination import dev.jdtech.jellyfin.destinations.AddServerScreenDestination
import com.nomadics9.ananas.destinations.LoginScreenDestination import dev.jdtech.jellyfin.destinations.LoginScreenDestination
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.viewmodels.MainViewModel import dev.jdtech.jellyfin.viewmodels.MainViewModel
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas package dev.jdtech.jellyfin
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
@ -9,11 +9,11 @@ import com.ramcosta.composedestinations.annotation.ActivityDestination
import com.ramcosta.composedestinations.manualcomposablecalls.composable import com.ramcosta.composedestinations.manualcomposablecalls.composable
import com.ramcosta.composedestinations.scope.resultRecipient import com.ramcosta.composedestinations.scope.resultRecipient
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.destinations.PlayerActivityDestination import dev.jdtech.jellyfin.destinations.PlayerActivityDestination
import com.nomadics9.ananas.destinations.PlayerScreenDestination import dev.jdtech.jellyfin.destinations.PlayerScreenDestination
import com.nomadics9.ananas.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import com.nomadics9.ananas.ui.PlayerScreen import dev.jdtech.jellyfin.ui.PlayerScreen
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
data class PlayerActivityNavArgs( data class PlayerActivityNavArgs(
val items: ArrayList<PlayerItem>, val items: ArrayList<PlayerItem>,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.ui package dev.jdtech.jellyfin.ui
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -38,13 +38,13 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LoginScreenDestination import dev.jdtech.jellyfin.destinations.LoginScreenDestination
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings import dev.jdtech.jellyfin.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents import dev.jdtech.jellyfin.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.AddServerEvent import dev.jdtech.jellyfin.viewmodels.AddServerEvent
import com.nomadics9.ananas.viewmodels.AddServerViewModel import dev.jdtech.jellyfin.viewmodels.AddServerViewModel
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@Destination @Destination
@Composable @Composable
@ -115,7 +115,7 @@ private fun AddServerScreenLayout(
}, },
singleLine = true, singleLine = true,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
autoCorrect = false, autoCorrectEnabled = false,
keyboardType = KeyboardType.Uri, keyboardType = KeyboardType.Uri,
imeAction = ImeAction.Go, imeAction = ImeAction.Go,
), ),

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.ui package dev.jdtech.jellyfin.ui
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@ -6,6 +6,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -19,31 +22,28 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.MovieScreenDestination import dev.jdtech.jellyfin.destinations.MovieScreenDestination
import com.nomadics9.ananas.destinations.PlayerActivityDestination import dev.jdtech.jellyfin.destinations.PlayerActivityDestination
import com.nomadics9.ananas.destinations.ShowScreenDestination import dev.jdtech.jellyfin.destinations.ShowScreenDestination
import com.nomadics9.ananas.models.FindroidEpisode import dev.jdtech.jellyfin.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.models.HomeItem import dev.jdtech.jellyfin.models.HomeItem
import com.nomadics9.ananas.ui.components.Direction import dev.jdtech.jellyfin.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard import dev.jdtech.jellyfin.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyHomeItems import dev.jdtech.jellyfin.ui.dummy.dummyHomeItems
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings import dev.jdtech.jellyfin.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents import dev.jdtech.jellyfin.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.HomeViewModel import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@Destination @Destination
@Composable @Composable
@ -107,7 +107,7 @@ private fun HomeScreenLayout(
} }
else -> Unit else -> Unit
} }
TvLazyColumn( LazyColumn(
contentPadding = PaddingValues(bottom = MaterialTheme.spacings.large), contentPadding = PaddingValues(bottom = MaterialTheme.spacings.large),
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -122,7 +122,7 @@ private fun HomeScreenLayout(
modifier = Modifier.padding(start = MaterialTheme.spacings.large), modifier = Modifier.padding(start = MaterialTheme.spacings.large),
) )
Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium)) Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium))
TvLazyRow( LazyRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.large), contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.large),
) { ) {
@ -145,7 +145,7 @@ private fun HomeScreenLayout(
modifier = Modifier.padding(start = MaterialTheme.spacings.large), modifier = Modifier.padding(start = MaterialTheme.spacings.large),
) )
Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium)) Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium))
TvLazyRow( LazyRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.large), contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.large),
) { ) {

View file

@ -1,7 +1,10 @@
package com.nomadics9.ananas.ui package dev.jdtech.jellyfin.ui
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -14,21 +17,18 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.tv.foundation.lazy.grid.TvGridCells
import androidx.tv.foundation.lazy.grid.TvLazyVerticalGrid
import androidx.tv.foundation.lazy.grid.items
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LibraryScreenDestination import dev.jdtech.jellyfin.destinations.LibraryScreenDestination
import com.nomadics9.ananas.models.CollectionType import dev.jdtech.jellyfin.models.CollectionType
import com.nomadics9.ananas.models.FindroidCollection import dev.jdtech.jellyfin.models.FindroidCollection
import com.nomadics9.ananas.ui.components.Direction import dev.jdtech.jellyfin.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard import dev.jdtech.jellyfin.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyCollections import dev.jdtech.jellyfin.ui.dummy.dummyCollections
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings import dev.jdtech.jellyfin.ui.theme.spacings
import com.nomadics9.ananas.viewmodels.MediaViewModel import dev.jdtech.jellyfin.viewmodels.MediaViewModel
import java.util.UUID import java.util.UUID
@Destination @Destination
@ -72,8 +72,8 @@ private fun LibrariesScreenLayout(
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
TvLazyVerticalGrid( LazyVerticalGrid(
columns = TvGridCells.Fixed(3), columns = GridCells.Fixed(3),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large), verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large),
contentPadding = PaddingValues( contentPadding = PaddingValues(

View file

@ -1,8 +1,11 @@
package com.nomadics9.ananas.ui package dev.jdtech.jellyfin.ui
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -15,27 +18,24 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import androidx.tv.foundation.lazy.grid.TvGridCells
import androidx.tv.foundation.lazy.grid.TvGridItemSpan
import androidx.tv.foundation.lazy.grid.TvLazyVerticalGrid
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LibraryScreenDestination import dev.jdtech.jellyfin.destinations.LibraryScreenDestination
import com.nomadics9.ananas.destinations.MovieScreenDestination import dev.jdtech.jellyfin.destinations.MovieScreenDestination
import com.nomadics9.ananas.destinations.ShowScreenDestination import dev.jdtech.jellyfin.destinations.ShowScreenDestination
import com.nomadics9.ananas.models.CollectionType import dev.jdtech.jellyfin.models.CollectionType
import com.nomadics9.ananas.models.FindroidFolder import dev.jdtech.jellyfin.models.FindroidFolder
import com.nomadics9.ananas.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow import dev.jdtech.jellyfin.models.FindroidShow
import com.nomadics9.ananas.ui.components.Direction import dev.jdtech.jellyfin.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard import dev.jdtech.jellyfin.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyMovies import dev.jdtech.jellyfin.ui.dummy.dummyMovies
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings import dev.jdtech.jellyfin.ui.theme.spacings
import com.nomadics9.ananas.viewmodels.LibraryViewModel import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import java.util.UUID import java.util.UUID
@ -86,8 +86,8 @@ private fun LibraryScreenLayout(
is LibraryViewModel.UiState.Loading -> Text(text = "LOADING") is LibraryViewModel.UiState.Loading -> Text(text = "LOADING")
is LibraryViewModel.UiState.Normal -> { is LibraryViewModel.UiState.Normal -> {
val items = uiState.items.collectAsLazyPagingItems() val items = uiState.items.collectAsLazyPagingItems()
TvLazyVerticalGrid( LazyVerticalGrid(
columns = TvGridCells.Fixed(5), columns = GridCells.Fixed(5),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default), verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.default * 2, vertical = MaterialTheme.spacings.large), contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.default * 2, vertical = MaterialTheme.spacings.large),
@ -95,7 +95,7 @@ private fun LibraryScreenLayout(
.fillMaxSize() .fillMaxSize()
.focusRequester(focusRequester), .focusRequester(focusRequester),
) { ) {
item(span = { TvGridItemSpan(this.maxLineSpan) }) { item(span = { GridItemSpan(this.maxLineSpan) }) {
Text( Text(
text = libraryName, text = libraryName,
style = MaterialTheme.typography.displayMedium, style = MaterialTheme.typography.displayMedium,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.ui package dev.jdtech.jellyfin.ui
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -41,15 +41,15 @@ import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.popUpTo import com.ramcosta.composedestinations.navigation.popUpTo
import com.nomadics9.ananas.NavGraphs import dev.jdtech.jellyfin.NavGraphs
import com.nomadics9.ananas.destinations.MainScreenDestination import dev.jdtech.jellyfin.destinations.MainScreenDestination
import com.nomadics9.ananas.models.UiText import dev.jdtech.jellyfin.models.UiText
import com.nomadics9.ananas.ui.theme.FindroidTheme import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings import dev.jdtech.jellyfin.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents import dev.jdtech.jellyfin.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.LoginEvent import dev.jdtech.jellyfin.viewmodels.LoginEvent
import com.nomadics9.ananas.viewmodels.LoginViewModel import dev.jdtech.jellyfin.viewmodels.LoginViewModel
import com.nomadics9.ananas.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@Destination @Destination
@Composable @Composable
@ -152,7 +152,7 @@ private fun LoginScreenLayout(
label = { Text(text = stringResource(id = CoreR.string.edit_text_username_hint)) }, label = { Text(text = stringResource(id = CoreR.string.edit_text_username_hint)) },
singleLine = true, singleLine = true,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
autoCorrect = false, autoCorrectEnabled = false,
keyboardType = KeyboardType.Text, keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next, imeAction = ImeAction.Next,
), ),
@ -175,7 +175,7 @@ private fun LoginScreenLayout(
label = { Text(text = stringResource(id = CoreR.string.edit_text_password_hint)) }, label = { Text(text = stringResource(id = CoreR.string.edit_text_password_hint)) },
singleLine = true, singleLine = true,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
autoCorrect = false, autoCorrectEnabled = false,
keyboardType = KeyboardType.Password, keyboardType = KeyboardType.Password,
imeAction = ImeAction.Go, imeAction = ImeAction.Go,
), ),

Some files were not shown because too many files have changed in this diff Show more