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:
jobs:
# 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
lint:
name: Lint
runs-on: ubuntu-22.04
if: startsWith(github.event.head_commit.message, 'build:')
steps:
- name: Checkout repository
uses: actions/checkout@v4
@ -39,45 +21,62 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
- 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.
- 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
with:
name: phone-libre-arm64-v8a.apk
path: ./app/phone/build/outputs/apk/libre/release/ananas-v0.10.3-0.14.2-libre-arm64-v8a.apk
# - name: Upload artifact phone-libre-armeabi-v7a-debug.apk
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: 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
# uses: actions/upload-artifact@v4
# with:
# name: tv-libre-x86-debug.apk
# path: ./app/tv/build/outputs/apk/libre/debug/tv-libre-x86-debug.apk
name: phone-libre-arm64-v8a-debug.apk
path: ./app/phone/build/outputs/apk/libre/debug/phone-libre-arm64-v8a-debug.apk
- name: Upload artifact phone-libre-armeabi-v7a-debug.apk
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
uses: actions/upload-artifact@v4
with:
name: 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
captures/
app/phone/libre/release/
.kotlin
.externalNativeBuild/
.cxx/
*.apk
*.dm
output.json
app/phone/libre/release/output-metadata.json
# IntelliJ
*.iml
@ -41,4 +37,3 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
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
| Home | Library | Movie | Season | Episode |
|-------------------------------------|-------------------------------------|---------------------------------|-----------------------------------|-------------------------------------|
@ -9,9 +24,8 @@ Personal fork
## Features
- Completely native interface
- Supported media items: movies, series, seasons, episodes
- Direct play and Transcoding
- Offline playback / downloads
- Transcoding Downloads (Original - 720p - 480p - 360p)
- Direct play only, (no transcoding)
- Offline playback / downloads
- ExoPlayer
- Video codecs: H.263, H.264, H.265, VP8, VP9, AV1
- Support depends on Android device
@ -35,8 +49,20 @@ Personal fork
- Websocket connection (Syncplay)
- 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
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 {
namespace = "com.nomadics9.ananas"
namespace = "dev.jdtech.jellyfin"
compileSdk = Versions.compileSdk
buildToolsVersion = Versions.buildTools
defaultConfig {
applicationId = "com.nomadics9.ananas"
applicationId = "dev.jdtech.jellyfin"
minSdk = Versions.minSdk
targetSdk = Versions.targetSdk
versionCode = Versions.appCode
versionName = Versions.appName
testInstrumentationRunner = "com.nomadics9.ananas.HiltTestRunner"
buildConfigField( "String", "DEFAULT_SERVER_ADDRESS", "\" \"")
buildConfigField( "String", "REQUEST_SERVER_ADDRESS", "\" \"")
buildConfigField("String", "FORGET_PASSWORD_ADDRESS", "\" \"")
buildConfigField("String", "UPDATE_ADDRESS", "\" \"")
testInstrumentationRunner = "dev.jdtech.jellyfin.HiltTestRunner"
}
applicationVariants.all {
@ -35,7 +31,7 @@ android {
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
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
}
}
@ -61,18 +57,10 @@ android {
flavorDimensions += "variant"
productFlavors {
create("libre") {
register("libre") {
dimension = "variant"
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 {
@ -134,7 +122,6 @@ dependencies {
implementation(libs.material)
implementation(libs.media3.ffmpeg.decoder)
implementation(libs.timber)
implementation(libs.markwon)
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.
#-renamesourcefileattribute SourceFile
-keepnames class com.nomadics9.ananas.models.PlayerItem
-keepnames class dev.jdtech.jellyfin.models.PlayerItem
# ProGuard thinks all SettingsFragments are unused
-keep class com.nomadics9.ananas.fragments.SettingsLanguageFragment
-keep class com.nomadics9.ananas.fragments.SettingsAppearanceFragment
-keep class com.nomadics9.ananas.fragments.SettingsDownloadsFragment
-keep class com.nomadics9.ananas.fragments.SettingsPlayerFragment
-keep class com.nomadics9.ananas.fragments.SettingsDeviceFragment
-keep class com.nomadics9.ananas.fragments.SettingsCacheFragment
-keep class com.nomadics9.ananas.fragments.SettingsNetworkFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsLanguageFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsAppearanceFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsDownloadsFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsPlayerFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsDeviceFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsCacheFragment
-keep class dev.jdtech.jellyfin.fragments.SettingsNetworkFragment
# These classes are from okhttp and are not used in Android
-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.content.Context

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas
package dev.jdtech.jellyfin
import android.util.Log
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.HiltAndroidTest
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.not
import org.junit.Before
@ -76,9 +76,9 @@ class MainActivityTest {
waitForElement(allOf(withText("Movies"), isDisplayed()))
onView(withText("Movies")).perform(click())
// Navigate to Battle of the Stars
waitForElement(allOf(withText("Battle of the Stars"), isDisplayed()))
onView(withText("Battle of the Stars")).perform(click())
// Navigate to The Boy in the Plastic Bubble
waitForElement(allOf(withText("The Boy in the Plastic Bubble"), isDisplayed()))
onView(withText("The Boy in the Plastic Bubble")).perform(click())
// Play the movie
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.ViewTreeObserver

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.di
package dev.jdtech.jellyfin.di
import android.content.Context
import androidx.room.Room
@ -7,8 +7,8 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import com.nomadics9.ananas.database.ServerDatabase
import com.nomadics9.ananas.database.ServerDatabaseDao
import dev.jdtech.jellyfin.database.ServerDatabase
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import javax.inject.Singleton
@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 androidx.appcompat.app.AppCompatDelegate
@ -14,7 +14,7 @@ import com.google.android.material.color.DynamicColorsOptions
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@HiltAndroidApp
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.view.View
@ -9,7 +9,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.updatePadding
import androidx.media3.session.MediaSession
import com.nomadics9.ananas.viewmodels.PlayerActivityViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
abstract class BasePlayerActivity : AppCompatActivity() {

View file

@ -1,20 +1,20 @@
package com.nomadics9.ananas
package dev.jdtech.jellyfin
import android.view.View
import android.widget.ImageView
import androidx.annotation.DrawableRes
import coil.load
import com.nomadics9.ananas.api.JellyfinApi
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.User
import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.User
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.BaseItemPerson
import org.jellyfin.sdk.model.api.ImageType
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) {
val itemId =

View file

@ -1,10 +1,12 @@
package com.nomadics9.ananas
package dev.jdtech.jellyfin
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
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.NavGraph
import androidx.navigation.fragment.NavHostFragment
@ -19,16 +21,12 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.google.android.material.navigation.NavigationBarView
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.database.ServerDatabaseDao
import com.nomadics9.ananas.databinding.ActivityMainBinding
import com.nomadics9.ananas.viewmodels.MainViewModel
import com.nomadics9.ananas.work.SyncWorker
import com.nomadics9.ananas.repository.JellyfinRepository
import com.nomadics9.ananas.utils.restart
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.databinding.ActivityMainBinding
import dev.jdtech.jellyfin.viewmodels.MainViewModel
import dev.jdtech.jellyfin.work.SyncWorker
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@ -40,9 +38,6 @@ class MainActivity : AppCompatActivity() {
@Inject
lateinit var database: ServerDatabaseDao
@Inject
lateinit var jellyfinRepository: JellyfinRepository
@Inject
lateinit var appPreferences: AppPreferences
@ -53,6 +48,21 @@ class MainActivity : AppCompatActivity() {
scheduleUserDataSync()
applyTheme()
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)
@ -76,18 +86,10 @@ class MainActivity : AppCompatActivity() {
val navView: NavigationBarView = binding.navView as NavigationBarView
if (appPreferences.offlineMode) {
appPreferences.isOffline = true
}
if (appPreferences.isOffline) {
navView.menu.clear()
navView.inflateMenu(CoreR.menu.bottom_nav_menu_offline)
}
if (!appPreferences.isOffline && appPreferences.autoOffline) {
testServerConnection()
}
setSupportActionBar(binding.mainToolbar)
// Passing each menu ID as a set of Ids because each
@ -108,7 +110,7 @@ class MainActivity : AppCompatActivity() {
navController.addOnDestinationChangedListener { _, destination, _ ->
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
}
if (destination.id == com.mikepenz.aboutlibraries.R.id.about_libraries_dest) {
@ -168,18 +170,4 @@ class MainActivity : AppCompatActivity() {
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.PictureInPictureParams
import android.content.Context
@ -34,25 +35,25 @@ import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.PlayerView
import androidx.navigation.navArgs
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 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 timber.log.Timber
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import com.nomadics9.ananas.models.VideoQuality
import dev.jdtech.jellyfin.core.R as CoreR
var isControlsLocked: Boolean = false
@AndroidEntryPoint
class PlayerActivity : BasePlayerActivity() {
@Inject
lateinit var appPreferences: AppPreferences
@ -61,8 +62,6 @@ class PlayerActivity : BasePlayerActivity() {
override val viewModel: PlayerActivityViewModel by viewModels()
private var previewScrubListener: PreviewScrubListener? = null
private var wasZoom: Boolean = false
private var oldSegment: FindroidSegment? = null
private var buttonPressed: Boolean = false
private val isPipSupported by lazy {
// Check if device has PiP feature
@ -111,13 +110,12 @@ class PlayerActivity : BasePlayerActivity() {
configureInsets(lockedControls)
if (appPreferences.playerGestures) {
playerGestureHelper =
PlayerGestureHelper(
appPreferences,
this,
binding.playerView,
getSystemService(AUDIO_SERVICE) as AudioManager,
)
playerGestureHelper = PlayerGestureHelper(
appPreferences,
this,
binding.playerView,
getSystemService(Context.AUDIO_SERVICE) as AudioManager,
)
}
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 subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
val speedButton = binding.playerView.findViewById<ImageButton>(R.id.btn_speed)
val skipButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro)
val watchCreditsButton = binding.playerView.findViewById<Button>(R.id.btn_watch_credits)
val skipIntroButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro)
val pipButton = binding.playerView.findViewById<ImageButton>(R.id.btn_pip)
val lockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_lockview)
val unlockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_unlock)
@ -144,101 +141,19 @@ class PlayerActivity : BasePlayerActivity() {
// Title
videoNameTextView.text = currentItemTitle
// Skip Button
if (currentSegment != oldSegment) buttonPressed = false
// Button Visibility and Text
when (currentSegment?.type) {
"intro" -> {
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
// Skip Intro button
skipIntroButton.isVisible = !isInPictureInPictureMode && currentIntro != null
skipIntroButton.setOnClickListener {
currentIntro?.let {
binding.playerView.player?.seekTo((it.introEnd * 1000).toLong())
}
}
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
previewScrubListener?.let {
it.currentTrickplay = currentTrickplay
}
playerGestureHelper?.let {
it.currentTrickplay = currentTrickplay
}
// Chapters
if (appPreferences.showChapterMarkers && currentChapters != null) {
currentChapters?.let { chapters ->
@ -276,8 +191,7 @@ class PlayerActivity : BasePlayerActivity() {
if (appPreferences.playerPipGesture) {
try {
setPictureInPictureParams(pipParams(event.isPlaying))
} catch (_: IllegalArgumentException) {
}
} catch (_: IllegalArgumentException) { }
}
}
}
@ -376,6 +290,7 @@ class PlayerActivity : BasePlayerActivity() {
viewModel.initializePlayer(args.items)
}
@SuppressLint("MissingSuperCall")
override fun onUserLeaveHint() {
super.onUserLeaveHint()
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 {
val displayAspectRatio = Rational(binding.playerView.width, binding.playerView.height)
val aspectRatio =
binding.playerView.player?.videoSize?.let {
Rational(
it.width.coerceAtMost((it.height * 2.39f).toInt()),
it.height.coerceAtMost((it.width * 2.39f).toInt()),
)
}
val aspectRatio = binding.playerView.player?.videoSize?.let {
Rational(
it.width.coerceAtMost((it.height * 2.39f).toInt()),
it.height.coerceAtMost((it.width * 2.39f).toInt()),
)
}
val sourceRectHint =
if (displayAspectRatio < aspectRatio!!) {
val space = ((binding.playerView.height - (binding.playerView.width.toFloat() / aspectRatio.toFloat())) / 2).toInt()
Rect(
0,
space,
binding.playerView.width,
(binding.playerView.width.toFloat() / aspectRatio.toFloat()).toInt() + space,
)
} else {
val space = ((binding.playerView.width - (binding.playerView.height.toFloat() * aspectRatio.toFloat())) / 2).toInt()
Rect(
space,
0,
(binding.playerView.height.toFloat() * aspectRatio.toFloat()).toInt() + space,
binding.playerView.height,
)
}
val sourceRectHint = if (displayAspectRatio < aspectRatio!!) {
val space = ((binding.playerView.height - (binding.playerView.width.toFloat() / aspectRatio.toFloat())) / 2).toInt()
Rect(
0,
space,
binding.playerView.width,
(binding.playerView.width.toFloat() / aspectRatio.toFloat()).toInt() + space,
)
} else {
val space = ((binding.playerView.width - (binding.playerView.height.toFloat() * aspectRatio.toFloat())) / 2).toInt()
Rect(
space,
0,
(binding.playerView.height.toFloat() * aspectRatio.toFloat()).toInt() + space,
binding.playerView.height,
)
}
val builder =
PictureInPictureParams
.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
val builder = PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setSourceRectHint(sourceRectHint)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setAutoEnterEnabled(enableAutoEnter)
@ -479,29 +390,25 @@ class PlayerActivity : BasePlayerActivity() {
playerGestureHelper?.updateZoomMode(false)
// Brightness mode Auto
window.attributes =
window.attributes.apply {
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
}
window.attributes = window.attributes.apply {
screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
}
}
false -> {
binding.playerView.useController = true
playerGestureHelper?.updateZoomMode(wasZoom)
// Override auto brightness
window.attributes =
window.attributes.apply {
screenBrightness =
if (appPreferences.playerBrightnessRemember) {
appPreferences.playerBrightness
} else {
Settings.System
.getInt(
contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
).toFloat() / 255
}
window.attributes = window.attributes.apply {
screenBrightness = if (appPreferences.playerBrightnessRemember) {
appPreferences.playerBrightness
} else {
Settings.System.getInt(
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.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.nomadics9.ananas.bindCardItemImage
import com.nomadics9.ananas.databinding.CollectionItemBinding
import com.nomadics9.ananas.models.FindroidCollection
import dev.jdtech.jellyfin.bindCardItemImage
import dev.jdtech.jellyfin.databinding.CollectionItemBinding
import dev.jdtech.jellyfin.models.FindroidCollection
class CollectionListAdapter(
private val onClickListener: (collection: FindroidCollection) -> Unit,
@ -47,4 +47,4 @@ class CollectionListAdapter(
}
holder.bind(collection)
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,11 @@
package com.nomadics9.ananas.dialogs
package dev.jdtech.jellyfin.dialogs
import android.content.Context
import android.os.Environment
import android.os.StatFs
import androidx.appcompat.app.AlertDialog
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(
context: Context,

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import android.text.method.LinkMovementMethod
@ -14,12 +14,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.nomadics9.ananas.BuildConfig
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.DiscoveredServerListAdapter
import com.nomadics9.ananas.databinding.FragmentAddServerBinding
import com.nomadics9.ananas.viewmodels.AddServerEvent
import com.nomadics9.ananas.viewmodels.AddServerViewModel
import dev.jdtech.jellyfin.adapters.DiscoveredServerListAdapter
import dev.jdtech.jellyfin.databinding.FragmentAddServerBinding
import dev.jdtech.jellyfin.viewmodels.AddServerEvent
import dev.jdtech.jellyfin.viewmodels.AddServerViewModel
import kotlinx.coroutines.launch
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
}
@ -140,16 +134,6 @@ class AddServerFragment : Fragment() {
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() {
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.view.LayoutInflater
@ -13,18 +13,18 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentFavoriteBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.CollectionViewModel
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.CollectionViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.view.LayoutInflater
@ -13,19 +13,19 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentDownloadsBinding
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.restart
import com.nomadics9.ananas.viewmodels.DownloadsEvent
import com.nomadics9.ananas.viewmodels.DownloadsViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.databinding.FragmentDownloadsBinding
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.restart
import dev.jdtech.jellyfin.viewmodels.DownloadsEvent
import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.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.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.R
import com.nomadics9.ananas.bindCardItemImage
import com.nomadics9.ananas.databinding.EpisodeBottomSheetBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.getStorageSelectionDialog
import com.nomadics9.ananas.dialogs.getVideoVersionDialog
import com.nomadics9.ananas.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem
import com.nomadics9.ananas.models.UiText
import com.nomadics9.ananas.models.isDownloaded
import com.nomadics9.ananas.models.isDownloading
import com.nomadics9.ananas.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.EpisodeBottomSheetEvent
import com.nomadics9.ananas.viewmodels.EpisodeBottomSheetViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.bindCardItemImage
import dev.jdtech.jellyfin.databinding.EpisodeBottomSheetBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.dialogs.getStorageSelectionDialog
import dev.jdtech.jellyfin.dialogs.getVideoVersionDialog
import dev.jdtech.jellyfin.models.FindroidSourceType
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.models.isDownloading
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetEvent
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.DateTime
import timber.log.Timber
@ -48,7 +48,7 @@ import java.util.UUID
import javax.inject.Inject
import android.R as AndroidR
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
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.view.LayoutInflater
@ -12,15 +12,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentFavoriteBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.FavoriteViewModel
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
import kotlinx.coroutines.launch
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.view.LayoutInflater
@ -19,21 +19,21 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.adapters.ViewListAdapter
import com.nomadics9.ananas.databinding.FragmentHomeBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.restart
import com.nomadics9.ananas.viewmodels.HomeViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.ViewListAdapter
import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.restart
import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class HomeFragment : Fragment() {
@ -74,12 +74,6 @@ class HomeFragment : Fragment() {
val searchView = search.actionView as SearchView
searchView.queryHint = getString(CoreR.string.search_hint)
val requests = menu.findItem(CoreR.id.action_requests)
requests.setOnMenuItemClickListener{
navigateToRequestsWebViewFragment()
true
}
search.setOnActionExpandListener(
object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
@ -208,7 +202,7 @@ class HomeFragment : Fragment() {
checkIfLoginRequired(uiState.error.message)
}
private fun navigateToLibraryFragment(view: com.nomadics9.ananas.models.View) {
private fun navigateToLibraryFragment(view: dev.jdtech.jellyfin.models.View) {
findNavController().navigate(
HomeFragmentDirections.actionNavigationHomeToLibraryFragment(
libraryId = view.id,
@ -257,10 +251,4 @@ class HomeFragment : Fragment() {
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.view.LayoutInflater
@ -19,25 +19,24 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.paging.LoadState
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.adapters.ViewItemPagingAdapter
import com.nomadics9.ananas.databinding.FragmentLibraryBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.SortDialogFragment
import com.nomadics9.ananas.models.FindroidBoxSet
import com.nomadics9.ananas.models.FindroidFolder
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.models.SortBy
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.LibraryViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.dialogs.SortDialogFragment
import dev.jdtech.jellyfin.models.FindroidBoxSet
import dev.jdtech.jellyfin.models.FindroidFolder
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.SortBy
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.SortOrder
import java.lang.IllegalArgumentException
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import androidx.recyclerview.widget.GridLayoutManager
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class LibraryFragment : Fragment() {
@ -63,11 +62,6 @@ class LibraryFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.itemsRecyclerView.layoutManager =
GridLayoutManager(context, preferences.spanCount)
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(
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.text.Html.fromHtml
import android.view.LayoutInflater
@ -18,18 +16,16 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.BuildConfig
import com.nomadics9.ananas.adapters.UserLoginListAdapter
import com.nomadics9.ananas.database.ServerDatabaseDao
import com.nomadics9.ananas.databinding.FragmentLoginBinding
import com.nomadics9.ananas.viewmodels.LoginEvent
import com.nomadics9.ananas.viewmodels.LoginViewModel
import io.noties.markwon.Markwon
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.UserLoginListAdapter
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.databinding.FragmentLoginBinding
import dev.jdtech.jellyfin.viewmodels.LoginEvent
import dev.jdtech.jellyfin.viewmodels.LoginViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class LoginFragment : Fragment() {
@ -82,17 +78,6 @@ class LoginFragment : Fragment() {
(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.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { uiState ->
@ -158,21 +143,7 @@ class LoginFragment : Fragment() {
binding.editTextPasswordLayout.isEnabled = true
uiState.disclaimer?.let { disclaimer ->
if (BuildConfig.FLAVOR == "Ananas") {
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)
}
binding.loginDisclaimer.text = fromHtml(disclaimer, 0)
}
}

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import android.view.LayoutInflater
@ -19,15 +19,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.CollectionListAdapter
import com.nomadics9.ananas.databinding.FragmentMediaBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidCollection
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.MediaViewModel
import dev.jdtech.jellyfin.adapters.CollectionListAdapter
import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidCollection
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.MediaViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.content.Intent
@ -21,32 +21,32 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.R
import com.nomadics9.ananas.adapters.PersonListAdapter
import com.nomadics9.ananas.bindItemBackdropImage
import com.nomadics9.ananas.databinding.FragmentMovieBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.dialogs.getStorageSelectionDialog
import com.nomadics9.ananas.dialogs.getVideoVersionDialog
import com.nomadics9.ananas.models.AudioCodec
import com.nomadics9.ananas.models.DisplayProfile
import com.nomadics9.ananas.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem
import com.nomadics9.ananas.models.UiText
import com.nomadics9.ananas.models.isDownloaded
import com.nomadics9.ananas.models.isDownloading
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.MovieEvent
import com.nomadics9.ananas.viewmodels.MovieViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.PersonListAdapter
import dev.jdtech.jellyfin.bindItemBackdropImage
import dev.jdtech.jellyfin.databinding.FragmentMovieBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.dialogs.getStorageSelectionDialog
import dev.jdtech.jellyfin.dialogs.getVideoVersionDialog
import dev.jdtech.jellyfin.models.AudioCodec
import dev.jdtech.jellyfin.models.DisplayProfile
import dev.jdtech.jellyfin.models.FindroidSourceType
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.models.isDownloading
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.MovieEvent
import dev.jdtech.jellyfin.viewmodels.MovieViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.view.LayoutInflater
@ -15,18 +15,18 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ViewItemListAdapter
import com.nomadics9.ananas.bindItemImage
import com.nomadics9.ananas.databinding.FragmentPersonDetailBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.PersonDetailViewModel
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.bindItemImage
import dev.jdtech.jellyfin.databinding.FragmentPersonDetailBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.PersonDetailViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.view.LayoutInflater
@ -13,15 +13,15 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.FavoritesListAdapter
import com.nomadics9.ananas.databinding.FragmentSearchResultBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.viewmodels.SearchResultViewModel
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel
import kotlinx.coroutines.launch
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.view.LayoutInflater
@ -12,12 +12,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ServerAddressAdapter
import com.nomadics9.ananas.databinding.FragmentServerAddressesBinding
import com.nomadics9.ananas.dialogs.AddServerAddressDialog
import com.nomadics9.ananas.dialogs.DeleteServerAddressDialog
import com.nomadics9.ananas.viewmodels.ServerAddressesEvent
import com.nomadics9.ananas.viewmodels.ServerAddressesViewModel
import dev.jdtech.jellyfin.adapters.ServerAddressAdapter
import dev.jdtech.jellyfin.databinding.FragmentServerAddressesBinding
import dev.jdtech.jellyfin.dialogs.AddServerAddressDialog
import dev.jdtech.jellyfin.dialogs.DeleteServerAddressDialog
import dev.jdtech.jellyfin.viewmodels.ServerAddressesEvent
import dev.jdtech.jellyfin.viewmodels.ServerAddressesViewModel
import kotlinx.coroutines.launch
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.view.LayoutInflater
@ -11,11 +11,11 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.adapters.ServerGridAdapter
import com.nomadics9.ananas.databinding.FragmentServerSelectBinding
import com.nomadics9.ananas.dialogs.DeleteServerDialogFragment
import com.nomadics9.ananas.viewmodels.ServerSelectEvent
import com.nomadics9.ananas.viewmodels.ServerSelectViewModel
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
import dev.jdtech.jellyfin.viewmodels.ServerSelectEvent
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
import kotlinx.coroutines.launch
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 androidx.appcompat.app.AppCompatDelegate
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsAppearanceFragment : PreferenceFragmentCompat() {
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.text.InputType
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsCacheFragment : PreferenceFragmentCompat() {
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 androidx.fragment.app.viewModels
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.viewmodels.SettingsDeviceViewModel
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.viewmodels.SettingsDeviceViewModel
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
class SettingsDeviceFragment : PreferenceFragmentCompat() {

View file

@ -1,8 +1,8 @@
package com.nomadics9.ananas.fragments
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsDownloadsFragment : PreferenceFragmentCompat() {
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.net.Uri
@ -7,7 +7,7 @@ import android.os.Bundle
import android.provider.Settings
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsLanguageFragment : PreferenceFragmentCompat() {
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.text.InputType
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.Constants
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.Constants
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsNetworkFragment : PreferenceFragmentCompat() {
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.os.Bundle
@ -7,7 +7,7 @@ import android.text.InputType
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
class SettingsPlayerFragment : PreferenceFragmentCompat() {
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.net.Uri
@ -18,29 +18,29 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.R
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.adapters.PersonListAdapter
import com.nomadics9.ananas.adapters.ViewItemListAdapter
import com.nomadics9.ananas.bindCardItemImage
import com.nomadics9.ananas.bindItemBackdropImage
import com.nomadics9.ananas.databinding.FragmentShowBinding
import com.nomadics9.ananas.dialogs.ErrorDialogFragment
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidSeason
import com.nomadics9.ananas.models.FindroidSourceType
import com.nomadics9.ananas.models.PlayerItem
import com.nomadics9.ananas.models.isDownloaded
import com.nomadics9.ananas.utils.checkIfLoginRequired
import com.nomadics9.ananas.utils.setIconTintColorAttribute
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel
import com.nomadics9.ananas.viewmodels.ShowEvent
import com.nomadics9.ananas.viewmodels.ShowViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.adapters.PersonListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.bindCardItemImage
import dev.jdtech.jellyfin.bindItemBackdropImage
import dev.jdtech.jellyfin.databinding.FragmentShowBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidSeason
import dev.jdtech.jellyfin.models.FindroidSourceType
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.models.isDownloaded
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.setIconTintColorAttribute
import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.viewmodels.ShowEvent
import dev.jdtech.jellyfin.viewmodels.ShowViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.core.R as CoreR
@AndroidEntryPoint
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.PreferenceHeaderFragmentCompat

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas.fragments
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import android.view.LayoutInflater
@ -12,12 +12,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.AppNavigationDirections
import com.nomadics9.ananas.adapters.UserListAdapter
import com.nomadics9.ananas.databinding.FragmentUsersBinding
import com.nomadics9.ananas.dialogs.DeleteUserDialogFragment
import com.nomadics9.ananas.viewmodels.UsersEvent
import com.nomadics9.ananas.viewmodels.UsersViewModel
import dev.jdtech.jellyfin.AppNavigationDirections
import dev.jdtech.jellyfin.adapters.UserListAdapter
import dev.jdtech.jellyfin.databinding.FragmentUsersBinding
import dev.jdtech.jellyfin.dialogs.DeleteUserDialogFragment
import dev.jdtech.jellyfin.viewmodels.UsersEvent
import dev.jdtech.jellyfin.viewmodels.UsersViewModel
import kotlinx.coroutines.launch
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.navigation.fragment.findNavController
import com.nomadics9.ananas.AppNavigationDirections
import dev.jdtech.jellyfin.AppNavigationDirections
import timber.log.Timber
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.content.res.Resources
import android.graphics.Bitmap
import android.media.AudioManager
import android.os.Build
import android.os.SystemClock
@ -20,20 +19,15 @@ import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import coil.transform.RoundedCornersTransformation
import com.nomadics9.ananas.AppPreferences
import com.nomadics9.ananas.Constants
import com.nomadics9.ananas.PlayerActivity
import com.nomadics9.ananas.isControlsLocked
import com.nomadics9.ananas.models.PlayerChapter
import com.nomadics9.ananas.models.Trickplay
import com.nomadics9.ananas.mpv.MPVPlayer
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.Constants
import dev.jdtech.jellyfin.PlayerActivity
import dev.jdtech.jellyfin.isControlsLocked
import dev.jdtech.jellyfin.models.PlayerChapter
import dev.jdtech.jellyfin.mpv.MPVPlayer
import timber.log.Timber
import kotlin.math.abs
import kotlinx.coroutines.Dispatchers
import coil.load
class PlayerGestureHelper(
private val appPreferences: AppPreferences,
private val activity: PlayerActivity,
@ -68,10 +62,6 @@ class PlayerGestureHelper(
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
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 val tapGestureDetector = GestureDetector(
@ -275,13 +265,6 @@ class PlayerGestureHelper(
activity.binding.progressScrubberLayout.visibility = View.VISIBLE
activity.binding.progressScrubberText.text = "${longToTimestamp(difference)} [${longToTimestamp(newPos, true)}]"
swipeGestureValueTrackerProgress = newPos
if (currentTrickplay != null) {
onMove(newPos)
} else {
activity.binding.imagePreviewGesture.visibility = View.GONE
}
swipeGestureProgressOpen = true
true
} else {
@ -488,28 +471,11 @@ class PlayerGestureHelper(
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 {
if (appPreferences.playerBrightnessRemember) {
activity.window.attributes.screenBrightness = appPreferences.playerBrightness
}
if (!appPreferences.playerTrickPlayGesture) {
activity.binding.imagePreviewGesture.visibility = View.GONE
}
updateZoomMode(appPreferences.playerStartMaximized)
@Suppress("ClickableViewAccessibility")

View file

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

View file

@ -37,16 +37,7 @@
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
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>
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -40,17 +39,8 @@
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/main_toolbar"
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>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -16,11 +16,8 @@
<LinearLayout
android:id="@+id/progress_scrubber_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="64dp"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/overlay_background"
android:clickable="false"
android:gravity="center"
@ -28,20 +25,10 @@
android:visibility="gone"
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
android:id="@+id/progress_scrubber_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
android:textColor="@android:color/white"
tools:text="+00:00:10 [00:00:20]" />

View file

@ -10,7 +10,6 @@
android:layout_height="match_parent"
android:layout_gravity="center">
<!-- Video surface will be inserted as the first child of the content frame. -->
<View
@ -49,6 +48,24 @@
android:textColor="@android:color/white"
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.SubtitleView
@ -72,33 +89,4 @@
android:layout_height="match_parent"
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>

View file

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

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 {
namespace = "com.nomadics9.ananas"
namespace = "dev.jdtech.jellyfin"
compileSdk = Versions.compileSdk
buildToolsVersion = Versions.buildTools
defaultConfig {
applicationId = "com.nomadics9.ananas"
applicationId = "dev.jdtech.jellyfin"
minSdk = Versions.minSdk
targetSdk = Versions.targetSdk
@ -81,15 +81,14 @@ ktlint {
}
dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(projects.core)
implementation(projects.data)
implementation(projects.preferences)
implementation(projects.player.core)
implementation(projects.player.video)
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.material3)
implementation(libs.androidx.core)
@ -99,7 +98,6 @@ dependencies {
implementation(libs.androidx.media3.ui)
implementation(libs.androidx.media3.session)
implementation(libs.androidx.paging.compose)
implementation(libs.androidx.tv.foundation)
implementation(libs.androidx.tv.material)
implementation(libs.coil.compose)
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 coil.ImageLoader

View file

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

View file

@ -1,4 +1,4 @@
package com.nomadics9.ananas
package dev.jdtech.jellyfin
import android.os.Bundle
import android.view.WindowManager
@ -9,11 +9,11 @@ import com.ramcosta.composedestinations.annotation.ActivityDestination
import com.ramcosta.composedestinations.manualcomposablecalls.composable
import com.ramcosta.composedestinations.scope.resultRecipient
import dagger.hilt.android.AndroidEntryPoint
import com.nomadics9.ananas.destinations.PlayerActivityDestination
import com.nomadics9.ananas.destinations.PlayerScreenDestination
import com.nomadics9.ananas.models.PlayerItem
import com.nomadics9.ananas.ui.PlayerScreen
import com.nomadics9.ananas.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.destinations.PlayerActivityDestination
import dev.jdtech.jellyfin.destinations.PlayerScreenDestination
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.ui.PlayerScreen
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
data class PlayerActivityNavArgs(
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.Column
@ -38,13 +38,13 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LoginScreenDestination
import com.nomadics9.ananas.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.AddServerEvent
import com.nomadics9.ananas.viewmodels.AddServerViewModel
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.destinations.LoginScreenDestination
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings
import dev.jdtech.jellyfin.utils.ObserveAsEvents
import dev.jdtech.jellyfin.viewmodels.AddServerEvent
import dev.jdtech.jellyfin.viewmodels.AddServerViewModel
import dev.jdtech.jellyfin.core.R as CoreR
@Destination
@Composable
@ -115,7 +115,7 @@ private fun AddServerScreenLayout(
},
singleLine = true,
keyboardOptions = KeyboardOptions(
autoCorrect = false,
autoCorrectEnabled = false,
keyboardType = KeyboardType.Uri,
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.PaddingValues
@ -6,6 +6,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
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.LaunchedEffect
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.tooling.preview.Preview
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.Text
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.MovieScreenDestination
import com.nomadics9.ananas.destinations.PlayerActivityDestination
import com.nomadics9.ananas.destinations.ShowScreenDestination
import com.nomadics9.ananas.models.FindroidEpisode
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.models.HomeItem
import com.nomadics9.ananas.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyHomeItems
import com.nomadics9.ananas.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.HomeViewModel
import com.nomadics9.ananas.viewmodels.PlayerItemsEvent
import com.nomadics9.ananas.viewmodels.PlayerViewModel
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.destinations.MovieScreenDestination
import dev.jdtech.jellyfin.destinations.PlayerActivityDestination
import dev.jdtech.jellyfin.destinations.ShowScreenDestination
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.HomeItem
import dev.jdtech.jellyfin.ui.components.Direction
import dev.jdtech.jellyfin.ui.components.ItemCard
import dev.jdtech.jellyfin.ui.dummy.dummyHomeItems
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings
import dev.jdtech.jellyfin.utils.ObserveAsEvents
import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerItemsEvent
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import dev.jdtech.jellyfin.core.R as CoreR
@Destination
@Composable
@ -107,7 +107,7 @@ private fun HomeScreenLayout(
}
else -> Unit
}
TvLazyColumn(
LazyColumn(
contentPadding = PaddingValues(bottom = MaterialTheme.spacings.large),
modifier = Modifier
.fillMaxSize()
@ -122,7 +122,7 @@ private fun HomeScreenLayout(
modifier = Modifier.padding(start = MaterialTheme.spacings.large),
)
Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium))
TvLazyRow(
LazyRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.large),
) {
@ -145,7 +145,7 @@ private fun HomeScreenLayout(
modifier = Modifier.padding(start = MaterialTheme.spacings.large),
)
Spacer(modifier = Modifier.height(MaterialTheme.spacings.medium))
TvLazyRow(
LazyRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
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.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.LaunchedEffect
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.tooling.preview.Preview
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 com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LibraryScreenDestination
import com.nomadics9.ananas.models.CollectionType
import com.nomadics9.ananas.models.FindroidCollection
import com.nomadics9.ananas.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyCollections
import com.nomadics9.ananas.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings
import com.nomadics9.ananas.viewmodels.MediaViewModel
import dev.jdtech.jellyfin.destinations.LibraryScreenDestination
import dev.jdtech.jellyfin.models.CollectionType
import dev.jdtech.jellyfin.models.FindroidCollection
import dev.jdtech.jellyfin.ui.components.Direction
import dev.jdtech.jellyfin.ui.components.ItemCard
import dev.jdtech.jellyfin.ui.dummy.dummyCollections
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings
import dev.jdtech.jellyfin.viewmodels.MediaViewModel
import java.util.UUID
@Destination
@ -72,8 +72,8 @@ private fun LibrariesScreenLayout(
val focusRequester = remember { FocusRequester() }
TvLazyVerticalGrid(
columns = TvGridCells.Fixed(3),
LazyVerticalGrid(
columns = GridCells.Fixed(3),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.large),
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.PaddingValues
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.LaunchedEffect
import androidx.compose.runtime.collectAsState
@ -15,27 +18,24 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.PagingData
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.Text
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.nomadics9.ananas.destinations.LibraryScreenDestination
import com.nomadics9.ananas.destinations.MovieScreenDestination
import com.nomadics9.ananas.destinations.ShowScreenDestination
import com.nomadics9.ananas.models.CollectionType
import com.nomadics9.ananas.models.FindroidFolder
import com.nomadics9.ananas.models.FindroidItem
import com.nomadics9.ananas.models.FindroidMovie
import com.nomadics9.ananas.models.FindroidShow
import com.nomadics9.ananas.ui.components.Direction
import com.nomadics9.ananas.ui.components.ItemCard
import com.nomadics9.ananas.ui.dummy.dummyMovies
import com.nomadics9.ananas.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings
import com.nomadics9.ananas.viewmodels.LibraryViewModel
import dev.jdtech.jellyfin.destinations.LibraryScreenDestination
import dev.jdtech.jellyfin.destinations.MovieScreenDestination
import dev.jdtech.jellyfin.destinations.ShowScreenDestination
import dev.jdtech.jellyfin.models.CollectionType
import dev.jdtech.jellyfin.models.FindroidFolder
import dev.jdtech.jellyfin.models.FindroidItem
import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.ui.components.Direction
import dev.jdtech.jellyfin.ui.components.ItemCard
import dev.jdtech.jellyfin.ui.dummy.dummyMovies
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import java.util.UUID
@ -86,8 +86,8 @@ private fun LibraryScreenLayout(
is LibraryViewModel.UiState.Loading -> Text(text = "LOADING")
is LibraryViewModel.UiState.Normal -> {
val items = uiState.items.collectAsLazyPagingItems()
TvLazyVerticalGrid(
columns = TvGridCells.Fixed(5),
LazyVerticalGrid(
columns = GridCells.Fixed(5),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacings.default),
contentPadding = PaddingValues(horizontal = MaterialTheme.spacings.default * 2, vertical = MaterialTheme.spacings.large),
@ -95,7 +95,7 @@ private fun LibraryScreenLayout(
.fillMaxSize()
.focusRequester(focusRequester),
) {
item(span = { TvGridItemSpan(this.maxLineSpan) }) {
item(span = { GridItemSpan(this.maxLineSpan) }) {
Text(
text = libraryName,
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.Column
@ -41,15 +41,15 @@ import androidx.tv.material3.Text
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.popUpTo
import com.nomadics9.ananas.NavGraphs
import com.nomadics9.ananas.destinations.MainScreenDestination
import com.nomadics9.ananas.models.UiText
import com.nomadics9.ananas.ui.theme.FindroidTheme
import com.nomadics9.ananas.ui.theme.spacings
import com.nomadics9.ananas.utils.ObserveAsEvents
import com.nomadics9.ananas.viewmodels.LoginEvent
import com.nomadics9.ananas.viewmodels.LoginViewModel
import com.nomadics9.ananas.core.R as CoreR
import dev.jdtech.jellyfin.NavGraphs
import dev.jdtech.jellyfin.destinations.MainScreenDestination
import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.ui.theme.FindroidTheme
import dev.jdtech.jellyfin.ui.theme.spacings
import dev.jdtech.jellyfin.utils.ObserveAsEvents
import dev.jdtech.jellyfin.viewmodels.LoginEvent
import dev.jdtech.jellyfin.viewmodels.LoginViewModel
import dev.jdtech.jellyfin.core.R as CoreR
@Destination
@Composable
@ -152,7 +152,7 @@ private fun LoginScreenLayout(
label = { Text(text = stringResource(id = CoreR.string.edit_text_username_hint)) },
singleLine = true,
keyboardOptions = KeyboardOptions(
autoCorrect = false,
autoCorrectEnabled = false,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next,
),
@ -175,7 +175,7 @@ private fun LoginScreenLayout(
label = { Text(text = stringResource(id = CoreR.string.edit_text_password_hint)) },
singleLine = true,
keyboardOptions = KeyboardOptions(
autoCorrect = false,
autoCorrectEnabled = false,
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Go,
),

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