Compare commits

...

96 commits

Author SHA1 Message Date
nomadics9
e009b86153 build: 16 version-0.10.6-0.14.2
Some checks failed
Build / Assemble (push) Has been cancelled
2024-07-26 05:13:37 +03:00
nomadics9
d669dcb618 hotfix 2024-07-26 05:13:08 +03:00
nomadics9
ff5bce074e build: 16 version-0.10.6-0.14.2 2024-07-26 05:02:31 +03:00
nomadics9
d85684317b feat: very simple update checker 2024-07-26 04:58:50 +03:00
nomadics9
3cc52938f2 bugfix: Download season dialog strings 2024-07-26 02:42:48 +03:00
nomadics9
3ff71ae489 build: 15 version-0.10.5-0.14.2 2024-07-21 06:23:54 +03:00
nomadics9
b511b26aa1 feat: Markdown support for disclamer 2024-07-21 06:18:21 +03:00
nomadics9
059b17af9a personal 2024-07-21 05:16:04 +03:00
nomadics9
c293c906d4 build: 15 version-0.10.5-0.14.2 2024-07-21 04:38:32 +03:00
nomadics9
b5d31a6c72 feat: select transcoding codec in network settings / code: clean up, refactors & rework alot of transcoding stuff / bugfixes: mainly deviceId 2024-07-21 04:18:08 +03:00
nomadics9
89d5a332d1 build: 14 bugfixes 2024-07-20 06:42:02 +03:00
nomadics9
4e8ee15d0a bugfixes: getDeviceId() / code: New Enum VideoQuality 2024-07-20 06:31:53 +03:00
nomadics9
36dd8480e1 flow: Version 2024-07-18 23:33:28 +03:00
nomadics9
a6c1ef15b7 hotfix 2024-07-18 07:23:34 +03:00
nomadics9
75ea1dc43a hotfix 2024-07-18 07:11:32 +03:00
nomadics9
2b2e6ce58b build: 12 Embedded subs in downloaded transcode 2024-07-18 06:52:52 +03:00
nomadics9
09427e1de0 feat: Embedded subs in downloaded transcode 2024-07-18 05:20:34 +03:00
nomadics9
fbf4c185f0 build: 11 embedded subs 2024-07-18 04:08:17 +03:00
nomadics9
c84ec082be feat: Embedded subtitle in transcoding stream / bugfixes: Download quality dialog loop / code: clean up 2024-07-18 03:59:38 +03:00
nomadics9
5a8403f6a9 Build: 10 Transcoding Download Merge 2024-07-17 06:30:54 +03:00
nomadics9
362201eddf feat: Transcoding Download / code: Cleanup
Some checks failed
Build / Assemble (push) Has been cancelled
2024-07-17 05:58:26 +03:00
nomadics9
afbf68937d README update 2024-07-14 22:44:52 +03:00
nomadics9
8139119c35 feat: Quality change in player (transcoded stream) 2024-07-14 21:54:21 +03:00
nomadics9
ab090a01d7 feat: Quality change in player 2024-07-14 01:38:34 +03:00
nomadics9
2253780903 fix: hide unused settings 2024-07-08 20:20:15 +03:00
nomadics9
3340ce4e58 refactor: string 2024-07-08 20:09:52 +03:00
nomadics9
ce66bbc0b2 fix: banner on lightmode 2024-07-08 19:52:02 +03:00
nomadics9
e122ef303e fix: auto offline default true 2024-07-08 18:43:45 +03:00
nomadics9
dedb99b73d feat: auto offline 2024-07-08 18:39:04 +03:00
nomadics9
4bf8e6b697 build: Version 7 Built 2024-07-05 20:12:55 +03:00
nomadics9
89fb969d67 build: Version 7 Built 2024-07-05 20:12:37 +03:00
nomadics9
eac3eaa3f3 feat: Rows preferance in libraries view set default to 3 / settings defaults fix 2024-07-05 20:00:21 +03:00
nomadics9
e6c49ea660 flow: fix 2024-07-05 00:01:53 +03:00
nomadics9
44c1b51553 flow: fix 2024-07-04 23:45:13 +03:00
nomadics9
e18e73cca8 flow: fix 2024-07-04 23:24:34 +03:00
nomadics9
5156333a95 flow: Testing 2024-07-04 23:17:54 +03:00
nomadics9
eface29638 build: Version 6 Built 2024-07-04 20:15:48 +03:00
nomadics9
44b6e915ba feat: merged Skip Credits 2024-07-04 20:06:37 +03:00
nomadics9
a1cbea0b92 refactor:package name 2024-07-03 23:40:52 +03:00
nomadics9
e987ac477d feat: Download Season + Download unwatched episodes only dialogue 2024-07-02 17:10:59 +03:00
nomadics9
9baa84e1e7 feat: trickplay on gesture 2024-06-30 22:48:36 +03:00
nomadics9
ed69473e26 feat: trickplay on gesture 2024-06-30 22:48:10 +03:00
nomadics9
73d1b7c099 builds ignore 2024-06-30 21:39:08 +03:00
nomadics9
f269bea184 feat: requests in-app 2024-06-30 21:33:00 +03:00
nomadics9
5c283982bb fix: settings logos 2024-06-30 15:47:50 +03:00
nomadics9
cb4518c86e fix: amoled toggle default true / add: logos for settings without a logo / add: default server / refactor: tv package 2024-06-30 15:11:42 +03:00
nomadics9
2d141b6f0f Fix middle top logo for wide screens 2024-06-30 10:08:13 +03:00
nomadics9
ab19866899
Update README.md 2024-06-29 20:31:03 +03:00
nomadics9
168cfcd19f Add logo on middle topbar and amoled prefrence default true 2024-06-29 19:18:22 +03:00
nomadics9
288c96709e refactor package name 2024-06-29 15:44:54 +03:00
nomadics9
8b5c19b957 refactor app_name 2024-06-29 15:19:49 +03:00
nomadics9
be1da2eb7b gitignore .kotlin 2024-06-29 15:17:03 +03:00
nomadics9
61af530c89 strings 2024-06-29 03:26:18 +03:00
nomadics9
7b508fa456 appCode = 1 2024-06-29 03:19:31 +03:00
nomadics9
fcb58ef8ad personal personlization init 2024-06-29 03:07:12 +03:00
cd16b
09f3d218c1 Remove buttons colors and fix lint 2024-06-25 23:28:54 +02:00
cd16b
350afaa8a9 Fix buttons still visible 2024-06-24 19:09:27 +02:00
cd16b
ba2f9d9708 Update Database 2024-06-24 18:54:10 +02:00
Freya
e74a86da24
Merge branch 'jarnedemeulemeester:main' into auto-offline-mode 2024-06-24 13:32:53 +00:00
cd16b
5ab65062e6 Merge remote-tracking branch 'refs/remotes/origin/main' into Skip-credit
# Conflicts:
#	core/src/main/java/dev/jdtech/jellyfin/utils/DownloaderImpl.kt
#	core/src/main/res/values-it/strings.xml
#	core/src/main/res/values/strings.xml
#	data/schemas/dev.jdtech.jellyfin.database.ServerDatabase/5.json
#	data/src/main/java/dev/jdtech/jellyfin/database/ServerDatabase.kt
#	data/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryImpl.kt
#	data/src/main/java/dev/jdtech/jellyfin/repository/JellyfinRepositoryOfflineImpl.kt
#	player/video/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt
2024-06-24 12:53:47 +02:00
cd16b
6095c97704 Materia3 buttons and WatchCredits button 2024-06-24 12:01:50 +02:00
cd16b
91cccc55a7 Improve skipButton visibility/usability 2024-06-21 14:36:11 +02:00
cd16b
df984fb24b FindroidSegment 2024-06-20 23:59:24 +02:00
Cd16d
9f3be43eac
Merge branch 'main' into Skip-credit 2024-06-20 10:03:22 +02:00
Cd16d
0999823d6d
Merge branch 'jarnedemeulemeester:main' into Skip-credit 2024-06-02 13:34:01 +02:00
Cd16d
e10ae9c487
Merge branch 'main' into Skip-credit 2024-04-15 20:07:07 +02:00
cd16b
ce9eed6344 fix skipButton hide on click 2024-03-05 16:27:21 +01:00
Cd16d
d4e6351a2d
Merge branch 'main' into Skip-credit 2024-03-04 00:29:24 +01:00
cd16b
f75079f720 fix skipButton still visible after intro end 2024-03-04 00:28:26 +01:00
Cd16d
4a3afe62ef
Update strings-da 2024-02-25 16:45:36 +01:00
Cd16d
50b39d6658
Update strings.xml 2024-02-25 16:36:21 +01:00
Cd16d
3c6e03db89
Merge branch 'main' into Skip-credit 2024-02-25 16:32:47 +01:00
cd16b
05730a513c change text hasNextMediaItem() false 2024-01-23 11:51:45 +01:00
cd16b
674699aeab fix code 2024-01-23 09:58:26 +01:00
cd16b
f9454029f7 clean code 2024-01-22 22:59:04 +01:00
cd16b
2b9831af56 fix next episode no credits 2024-01-22 19:59:01 +01:00
cd16b
6402a6a0c4 fix and change pref_player_intro_skipper_summary 2024-01-22 19:16:44 +01:00
cd16b
a740d3fc71 fix lint 2024-01-22 17:29:44 +01:00
cd16b
9711f4c4fb Close player on the last episode of a series 2024-01-22 17:25:12 +01:00
cd16b
916d71a085 fix PlayerActivityViewModel.kt 2024-01-22 15:40:53 +01:00
cd16b
bdef58d433 Merge remote-tracking branch 'origin/Skip-credit' into Skip-credit 2024-01-22 15:25:16 +01:00
cd16b
4a3a22de37 fix PlayerActivityViewModel.kt 2024-01-22 15:24:49 +01:00
Cd16d
6a917be93f
Merge branch 'jarnedemeulemeester:main' into Skip-credit 2024-01-22 14:20:12 +01:00
cd16b
7f02f3de0a fix lint 2024-01-22 14:14:28 +01:00
cd16b
14eb313b1e fix lint 2024-01-22 13:45:41 +01:00
cd16b
85ff16d843 skip credits 2024-01-22 13:41:40 +01:00
cd16b
92eaefe6e1 skip credits 2024-01-22 13:40:45 +01:00
Freya Winters
216092888a Fix linting issue 2024-01-15 09:02:01 +01:00
Freya
a972832aae
Merge branch 'main' into main 2024-01-11 20:03:49 +00:00
Freya Winters
722267ced2 Merge remote-tracking branch 'upstream/main' 2023-12-28 15:48:58 +01:00
Freya Winters
da64c968bc Fix manual offline mode 2023-10-28 13:38:09 +02:00
Jesper Winters
b5f5a6eaed Fix linting issue 2023-10-27 15:49:20 +02:00
Jesper Winters
49d52f9713 Fix linting issues 2023-10-27 15:37:37 +02:00
Jcuhfehl
5b38bdb1c1 Fix linting issues 2023-10-27 15:37:14 +02:00
Jesper Winters
689c5cff3f Clean up 2023-10-27 15:37:07 +02:00
Jesper Winters
3db0f57437 Add option to turn on offline mode automatically 2023-10-27 15:35:07 +02:00
350 changed files with 6692 additions and 2996 deletions

View file

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

5
.gitignore vendored
View file

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

View file

@ -1,7 +1,6 @@
This privacy policy pertains the Findroid app. This privacy policy pertains the Ananas app.
Findroid does not collect or access any personal information. No identifying information or user data of any kind is made available to third-parties. Ananas 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 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. 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.
Findroid is published by Jarne Demeulemeester. Inquiries can be submitted to jarnedemeulemeester@gmail.com.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,120 +0,0 @@
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,59 +0,0 @@
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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,65 @@
{
"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

@ -0,0 +1,87 @@
{
"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

@ -0,0 +1,87 @@
{
"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 dev.jdtech.jellyfin package com.nomadics9.ananas
import android.app.Application import android.app.Application
import coil.ImageLoader import coil.ImageLoader

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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